pull reads every schedule on the revision and writes them back out as manifest entries — turning the revision
ids, session ids and org-qualified blob references the platform stores into the names a person wrote in the first
place.
It is how an existing scheduler blob becomes a file you can version-control, and how a schedule someone created
in the browser gets adopted into a manifest that already exists.
Synopsis
It overwrites the manifest, with nothing to stop it
There is no--force and no --dry-run. The file named by -f is replaced with a freshly generated document
every time, and the run before it did not ask.
This is worth stating plainly because the file being replaced is your own hand-authored manifest, not a
generated artifact. Its comments, its key order, its quoting, and any entry it declares that the revision does not
actually carry are all gone afterwards. Against a revision holding no schedules at all, a two-entry manifest comes
back as:
version so it cannot be read back as a
float and an invocation_time so it cannot be read back as a timestamp, and leaving alone what does not need it.
The document is regenerated rather than merged, deliberately: the point of pull is to report what the platform
actually holds, and folding stale local entries back in would defeat it. It also means pull is not the command
to reach for when you want to know what would change. That is
diff, which writes nothing at all. Run it first, and keep the manifest in
version control so a surprising pull is one git checkout away.
Elsewhere in this CLI a local write is guarded: blobhub workflow eject refuses
outright with EJECT_TARGET_EXISTS if any destination file exists. pull does not, here or in the workflow
group — a pull’s whole purpose is to replace the file, where an eject’s is to create one.
What it does
- Loads the manifest and resolves the scheduler blob and revision. This is a read, so a
commit-phase revision pulls fine. - Pages
list_schedulesto completion. - Sets aside every schedule the platform stored with no alias — they cannot be expressed as manifest entries.
- Reverses each remaining schedule’s stored target back into symbols.
- Writes a fresh document to the manifest path, entries sorted by alias.
--json the payload carries blob, schedules (the aliases written), unmanageable (the ids skipped)
and path, alongside the envelope’s schema_version.
Reversal, field by field
A stored schedule records coordinates. A manifest declares names. Each of the four target fields is reversed by its own rule:
A pinned
target.revision survives a pull. Nothing in a stored schedule records whether the manifest wrote
latest or a literal UUID — the platform keeps only the resolved id — so pull reads that intent back from the
manifest entry it is refreshing, matched by alias. A schedule the manifest never carried has no such intent to
recover, and comes back as latest when its target revision still leads and as a literal id when it does not.
The written entry always carries type: workflow in its target, even where the manifest you started from omitted
it. The two are equivalent: workflow is both the default and the only accepted value.
It adopts drift
pull writes out every aliased schedule on the revision, not only the ones your manifest already names. A
schedule created in the web UI, or by a colleague’s deploy, is drift before the pull and an ordinary manifest
entry after it.
That is the intended path from a browser-managed scheduler blob to a declarative one, and it is the only
resolution for drift that this group offers — the alternative, removal, needs blobhub scheduler prune, which is
not yet available. It is also the reason to read the version-control changes to your own manifest afterwards:
adopting drift is a decision, and pull makes it for every aliased schedule at once.
Aliasless schedules are skipped, not silently
A schedule the platform stored with no alias has no manifest representation.alias is a schedule entry’s
required identity, so writing one out would produce a file this CLI’s own loader rejects — and inventing an alias
for it would make the next deploy create a second, separate schedule beside
the one already firing.
So it is left out of the document and named on its own output line instead, by id, under unmanageable in the
JSON payload. It is still firing; dropping it in silence is how an operator ends up with a schedule they cannot
see. To bring one under management, give it an alias through the platform’s
Update Schedule operation and pull again.
pull reads the target blobs too
Reversal is not a local transformation. For every schedule, pull reads the target revision, the target blob and
the target session — so it needs read access to every blob any schedule points at, not just to the scheduler blob
itself.
One unreachable target is enough to stop the whole command. Every entry is reversed before anything is written, so
a target that cannot be read aborts the pull with the platform’s 403 surfaced as AUTH_INVALID, and the
manifest on disk is left exactly as it was. Use ls when you only need to see what
the revision holds: it reads the scheduler blob and nothing else.
pull and deploy are not inverses
Worth repeating here, because the file-in, file-out symmetry of workflow pull/push makes it tempting to assume
otherwise: a round trip through pull and deploy is not the identity.
deploy resolves symbols into UUIDs and may create a session that did not exist; nothing in a manifest
deletes a session, and pull will not undo it. See
Scheduler overview.
Errors
A skipped aliasless schedule is not an error: it is reported and the exit code stays
0.
Every code above, with its remediation, is in Error codes.
See also
blobhub scheduler diff— what a pull would overwrite, before it does.blobhub scheduler deploy— the other direction, and why it is not the inverse of this one.- Scheduler manifest — the document
pullwrites, field by field. blobhub workflow pull— the same verb in the group where it really does have a symmetric counterpart.

