Skip to content

Re-vendor the OpenAPI spec from conductor main - #8

Merged
devhawk merged 13 commits into
mainfrom
update-openapi-spec
Aug 18, 2026
Merged

Re-vendor the OpenAPI spec from conductor main#8
devhawk merged 13 commits into
mainfrom
update-openapi-spec

Conversation

@devhawk

@devhawk devhawk commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Re-vendors internal/api/openapi-3.1.json from conductor main and follows the API where it moved. The vendored spec is now byte-identical to what dbosdev/conductor:latest emits from its openapi subcommand.

Changes

  • Re-vendor the spec and regenerate the client and the OAuth-gate table. Most of the diff is client.gen.go.
  • permission list loses its client-side OAuth gate. conductor made listPermissions reachable without OAuth (dbos-inc/conductor#177), so the mode-aware error in internal/cli/permission.go was rejecting a call that now works. The gate table regenerates from x-dbos-requires-oauth, so this follows automatically — 16 gated operations remain.
  • Render applicationName in the workflow, queue, and schedule detail views.
  • Drop the spec-drift workflow and its test. That check belongs in conductor, next to the thing that changes.
  • Merge main (Run the integration tests against the published conductor image #7), picking up the image-based integration harness.
  • New api-compat CI job — see below.

api-compat

The existing drift check (make generate && git diff --exit-code) is hermetic: it proves the vendored spec and the committed client agree, which stays true no matter how far a deployed API moves. Nothing noticed when an upstream server changed shape.

This job regenerates the client from each upstream spec and builds it:

  • dbosdev/conductor:latest — the image the integration tier already runs against.
  • cloud's public /conductor/v2/openapi.json — not a separate spec. Cloud fetches conductor's live spec and rewrites only servers (~/cloud/controllers/public/conductor/openapi.go), so this tracks whichever conductor cloud has actually deployed, which lags the released image. The pair brackets the range of servers a released CLI must work against.

The cloud target fails today, for a known and benign reason. Production still advertises the applicationName filters conductor reverted in dbos-inc/conductor#187, so generating from it adds a params argument:

internal/cli/queue.go:49:60: not enough arguments in call to c.ListQueuesWithResponse
	have (context.Context, string, string)
	want (context.Context, string, string, *api.ListQueuesParams, ...)

That is a codegen incompatibility, not a runtime one — the parameters are optional and the current CLI talks to cloud fine. It also retroactively validates reverting --owner-app on this branch: had it stayed, it would compile against cloud and break against latest.

So the job is advisory (continue-on-error). It is sensitive by construction — oapi-codegen renders query parameters as a params-struct argument, so upstream adding one optional parameter fails the build here while the API stays backward compatible. Worth surfacing, not worth failing an unrelated PR over. It writes the spec delta to the step summary, and a failed fetch warns rather than reporting a compatibility problem we never observed.

It triggers on PR and push, like the rest of ci.yml — so it does not catch upstream changes that land while the repo is quiet. A schedule: trigger would; left off deliberately, since the daily drift check was dropped as belonging in conductor.

Verification

Run locally on the merged branch:

Check Result
make generate + drift clean
make lint pass
make test-integration 146 tests, 0 failures, 0 skipped
vendored spec vs live dbosdev/conductor:latest byte-identical
build against conductor latest spec pass
build against cloud spec fails as described above

Note for the reviewer

The history has add-then-undo churn — --owner-app is added in 5b49980 and reverted in dadb450; the spec-drift check is added in d697f10 and removed across 4c67e96 and 85826cd. Squash-merging is probably what you want.

🤖 Generated with Claude Code

devhawk and others added 11 commits August 12, 2026 13:00
The vendored spec predated conductor's autoscaling and domain-claim work
and its un-gating of listPermissions, so the generated client was missing
operations and the OAuth-gate table was wrong. Re-vendored with `make spec`
from conductor main (7601b14), which is byte-identical to the spec DBOS
Cloud serves at /conductor/v2/openapi.json, then `make generate`.

Seven new operations reach the client: getAutoscale, the three autoscaling
policy operations, and the three domain-claim operations. No command maps
to them yet; AGENTS.md now tracks them as unmapped surface.

Two consequential source changes:

- listQueues gained an optional applicationName parameter, so
  ListQueuesWithResponse takes a params argument. Passed nil, matching
  listSchedules and preserving today's behavior (the parameter defaults to
  the application in the path).

- listPermissions lost x-dbos-requires-oauth, so it left the gate table.
  Per the plan recorded in AGENTS.md, this removes `permission list`'s
  interim mode-aware refusal: the command now works against a no-auth
  deployment, resolving the org to `local` like every other unauthenticated
  command. Its no-auth unit test now asserts that rather than the refusal.

The gated-operation count goes 14 -> 16: listPermissions leaves, the three
domain-claim operations join.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tests for what the re-vendor actually changed:

- permission list against a real no-auth conductor (tier 3). The un-gating
  is the behavioral claim of the last commit, and a mock server cannot
  check it: what matters is that a no-auth conductor now registers the
  route at all. This is the test that would have caught the stale gate.

- queue list sends no applicationName. The CLI exposes no flag for the new
  filter, so it must stay unsent and let the server default it to the app
  in the path, rather than pinning it to an empty name.

- queue get renders applicationName when set and omits the row when null.
  This one needed a fix, not just a test: the field is new in the spec and
  queueFields() didn't project it, so `queue get` hid the owning app in
  exactly the case the field exists for -- several apps sharing a system
  database. Detail already omits empty fields, so nothing changes for the
  common null case.

And a daily spec-drift workflow. CI's existing check is hermetic: it proves
the generated client matches the vendored spec, never that the snapshot
still matches reality -- which is how the spec went stale unnoticed.
scripts/check-spec-drift.sh compares the vendored spec against the deployed
one at cloud.dbos.dev, ignoring the `servers` field cloud rewrites, and
reports the operation-level difference when they diverge.

Deployed is the only other copy compared. Conductor's own spec would catch
a re-vendor gap sooner, but that repo is private and this one is about to
be public, so CI would need a token to read it -- and conductor's main
legitimately runs ahead of the deployment between a merge and its release,
so the extra pair is mostly noise. Deployed is the copy dbosctl actually
talks to, so drift against it is always actionable. The job needs no
secrets: cloud serves the spec publicly.

Also available as `make check-spec` for local use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The re-vendor added applicationName to three response schemas -- Queue,
Schedule, and Workflow -- and the last commit only projected it on Queue.
So `schedule get` and `workflow get` still hid the owning application in
exactly the case the field exists for: several applications sharing one
system database. Same nullable *string shape, same fix, and Detail already
omits empty fields, so nothing changes for the common null case.

Tests cover both states for each, and were confirmed to fail without the
projection rather than merely pass with it.

Nothing else in the re-vendor reaches a command. The seven new operations
(getAutoscale, the autoscaling policy trio, the domain-claim trio) have no
CLI surface, so there is nothing here to test yet -- exercising the
generated client alone would test oapi-codegen, not this repo. The new
applicationName query parameters on listQueues/listSchedules/listWorkflows
are all passed nil, which the queue test already pins.

Also adds tests for scripts/check-spec-drift.sh, hermetically via httptest.
It is the only thing that notices a stale vendored spec, and its dangerous
failure mode is silent: a regression that made it always report "match"
would retire the check without breaking anything visibly. These pin that
drift is detected, that an unreachable server fails rather than passes, and
that the `servers` field cloud rewrites stays excluded -- which, if it
regressed, would fail the daily job every day until it was ignored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…uctor

Scheduling the vendored-vs-deployed comparison from this repo was always
the constrained version of the check. The useful comparison has three
sources -- conductor's own spec, this repo's vendored copy, and what is
deployed -- and conductor is the only one of the three repos that can read
all of them: it has its own source, this repo will be public, and the
deployed spec is already public. From here the conductor leg is
unreachable without handing a public repo's CI a token for a private one.

So this removes the daily workflow and the tests for the script it ran.
scripts/check-spec-drift.sh and `make check-spec` stay: still worth running
by hand after a re-vendor, and the natural starting point for the conductor
implementation. AGENTS.md now records why nothing is scheduled here, so the
workflow does not get helpfully re-added later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
listQueues takes an applicationName parameter that narrows results to
objects owned by a named application, plus unclaimed ones. That is a
different axis from -a/--app, which chooses the application the request is
addressed to and goes in the URL path, so it needs its own flag rather than
overloading that one.

--owner-app is defined once in addRequestFlags and read through
ownerAppFilter, so the endpoints that take the same parameter --
listSchedules and listWorkflows today, more later -- reuse the flag and the
helper instead of each inventing a spelling. AGENTS.md records the
convention, including that a list-valued endpoint should make the flag
repeatable rather than rename it.

Unset stays unset: conductor defaults the scope to the addressed
application, and no value means "everything", so the helper returns nil
rather than sending an empty name (which would scope to an application
called ""). Naming another application requires application.read on it, so
the filter cannot widen a caller's access.

The test declares the flag via addRequestFlags rather than hand-rolling it
-- the test command builder registers flags by hand, and a hand-rolled
declaration would pass while the real command forgot to register it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each sat in a table of ~20 uniform field projections that carries no other
comments, so annotating one entry implied it was special when its code is
identical to its neighbors. Both claims were also already stated elsewhere:
nullability on the generated field's own doc comment, and the omit-empty
behavior on output.Detail, where it holds for every field rather than just
this one. The tests still say it where it matters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This reverts commit 5b49980.

--app already carries the meaning users want: conductor defaults the
applicationName scope to the application in the path, so `queue list --app B`
already returns B's queues. --owner-app only did anything when it differed
from --app, which only helps when the app you want has no healthy executor
and a co-tenant sharing its system database does. That is too narrow to
justify a second app-shaped flag in the help text of an unshipped CLI.

Leaves the parameter unused. If the dead-executor case turns out to matter,
the better fix is probably in conductor -- falling back to a co-tenant's
executor -- which would serve the console and every generated client rather
than teaching each one a routing override.

The applicationName display work is unaffected: queue, schedule, and
workflow detail views still show the owning application when there is one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The workflow and its tests went earlier; this takes the script, the
check-spec target, and the docs with them, leaving nothing spec-drift
shaped in this repo.

Comparing the vendored spec against reality is still worth doing, but not
from here. This repo is about to be public and conductor never will be, so
the comparison that matters most -- vendored against conductor's own spec,
which catches a gap before it reaches a deployment -- is one a public repo
structurally cannot make. Conductor can reach all three copies. Where the
check lives, and what it compares, is a decision to make there rather than
something to leave a half-version of here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Picks up conductor b2b9285 (#187), which removes the six caller-settable
applicationName inputs -- the query parameters on listQueues, listSchedules
and listWorkflows, and the body properties on searchWorkflows and the two
aggregates operations. The three response fields stay, so `queue get`,
`schedule get` and `workflow get` still show a row's owning application.

The `nil` argument to ListQueuesWithResponse goes in the same commit, and
has to. applicationName was that operation's only query parameter, so
ListQueuesParams no longer exists and the fourth argument is now the
variadic reqEditors. `nil` is a valid RequestEditorFn, so the call still
compiles -- and then applyEditors invokes it, panicking on every
`dbosctl queue list`. Neither go build, go vet, nor the generate drift
check catches that; only TestRunQueueList does.

No operations were added or removed, and nothing became newly required.

Note that cloud has not deployed #187 yet, so this spec is briefly ahead of
what it serves. That is safe in this direction: every removed input was
optional and dbosctl never sent any of them, so a client generated from
this spec behaves identically against a server on either side of the
revert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The existing drift check is hermetic: `make generate && git diff --exit-code`
proves the vendored spec and the committed client agree, which stays true no
matter how far a deployed API moves. Nothing in CI notices when an upstream
server changes shape.

Add an api-compat job that regenerates the client from each upstream spec —
the dbosdev/conductor:latest image via its openapi subcommand, and cloud's
public /conductor/v2/openapi.json — and builds and unit-tests the result.
The two targets are not redundant: cloud serves conductor's spec with
`servers` repointed, from whichever conductor it has actually deployed, which
lags the released image. Today that gap is real — cloud still advertises the
applicationName filters conductor reverted in #187, and generating from it
adds a params argument to ListQueues that queue.go does not pass.

Advisory, not blocking. The job is sensitive by construction: oapi-codegen
renders query parameters as a params-struct argument, so upstream adding one
optional parameter breaks the build here while the API stays backward
compatible and the CLI keeps working at runtime. That is worth surfacing but
not worth failing an unrelated PR over, so it runs continue-on-error and
writes the spec delta to the step summary. A failed fetch warns rather than
reporting a compatibility problem we never observed.
@devhawk

devhawk commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Note, managed conductor is awaiting an update that has already been applied to latest conductor docker image. Managed conductor will be updated soon. We can choose to bypass failing check and merge anyway or wait until cloud is updated and rerun

@devhawk

devhawk commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

note, #9 will likely conflict w/ this PR. Probably best to let #9 go first and then fix up this PR + wait for updated managed conductor deployment

@devhawk
devhawk requested review from kraftp, maxdml and qianl15 August 13, 2026 21:00

@kraftp kraftp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It should pass + merge tomorrow

devhawk and others added 2 commits August 14, 2026 09:39
Conductor's "Cleanup Unused Endpoints" (dbos-inc/conductor#190) dropped the
restartWorkflow endpoint, and the autoscale request gained
maxOldApplicationVersions. Re-vendored with `make spec` from a local
conductor checkout at 0b85c57, then `make generate`.

The CLI's `workflow restart` command is already gone (#10), so nothing
referenced RestartWorkflowWithResponse. The OAuth-gate table is unchanged:
restart was never gated, so it stays at 16 operations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
devhawk added a commit to dbos-inc/dbos-docs that referenced this pull request Aug 17, 2026
Conductor's HTTP API and the `dbosctl` CLI had no documentation. This
adds two pages under **Deploy To Production** and links them from the
Conductor overview.

Both are new files, so the diff is additive — nothing existing is
rewritten beyond one bullet on `conductor.md`.

## The pages

**[Conductor API](docs/production/conductor-api.md)** — base URLs, the
three ways to get the OpenAPI spec, authentication, resource scoping,
the RFC 9457 error model, listing and filtering conventions, a map of
all 64 operations by area, the operations a no-auth self-hosted
deployment does not register, and how to generate a client.

**[dbosctl CLI Reference](docs/production/dbosctl.md)** — installation,
profiles, authentication, configuration precedence, output formats, exit
codes, and a command-by-command reference. Follows the shape of the
existing Cloud CLI reference page.

The sidebar is autogenerated from the directory, so no `sidebars.js`
change is needed.

## Where the content came from

Not from the READMEs. The endpoint reference, parameter lists, and error
shapes were read out of the OpenAPI spec, and the CLI reference was
generated from a recursive `--help` dump of a real build, then diffed
back against it mechanically — 38 leaf commands, no undocumented
commands, no invented flags.

Details that came from reading the implementation rather than the spec
text, because a reader would otherwise hit them as surprises:

- **Workflow mutations need a live executor.** Cancel, resume, restart,
fork, and delete are dispatched to a healthy connected executor rather
than applied in Conductor's database, so they fail outright when nothing
is running — resume and restart additionally need an executor on the
latest version.
- **The autoscaling operations require a DBOS Teams plan** and return
403 otherwise.
- **Getting the spec from the Conductor image needs `--entrypoint`.**
The image's entrypoint waits for Postgres and runs migrations without
forwarding arguments, so the obvious `docker run <image> openapi` hangs
waiting for a database the subcommand does not need.

## Two scoping calls worth a look

**The API is not "everything the console does."** An earlier draft said
so; it is wrong, because the console also drives DBOS Cloud — deploying
applications, provisioning databases, billing — none of which this API
touches. Both pages now scope it to the Conductor control plane, which
is what the console shows for a Conductor-connected application whether
it runs on your own infrastructure or on DBOS Cloud, and point the
cloud-specific operations at the pages that own them.

**Nothing is framed as versioned.** There is one public API; the
predecessor is internal. `v2` appears only in URLs and paths, where you
actually type it, and never in prose as though it were a choice.

## Verified against the shipped API

The pages track conductor `main` as of the `applicationName` revert
(dbos-inc/dbos-conductor#187) and the CLI as of dbos-inc/dbos-ctl#8 and
#9:

- the `listWorkflows` query parameters and the full workflow-search body
field list match the spec exactly;
- all 64 operations appear in the endpoint tables, and all 16
OAuth-gated operations are named by operation ID;
- `applicationName` is documented as a **response** field only — the
request filters were removed by #187;
- install instructions follow the `dbos-cli` → `dbos-ctl` repository
rename and lead with the new install script.

The site builds. The only broken-anchor warnings are pre-existing ones
on the Go reference pages, untouched by this branch.

## One sequencing note

The `dbosctl` page links to the CLI's releases page and its
`raw.githubusercontent.com` install script, both of which are only
reachable once that repository is public and a release is tagged. The
page carries an early-access admonition, so it is not misleading in the
meantime, but this probably wants to land alongside that flip.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devhawk
devhawk merged commit 145d368 into main Aug 18, 2026
10 of 12 checks passed
@devhawk
devhawk deleted the update-openapi-spec branch August 18, 2026 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants