pull reads every schedule on the revision and writes them back out as manifest entries — exactly as the platform
holds each one.
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
- Reads only the manifest’s
type,version,blobandrevision— never itsschedules:list — and resolves the scheduler blob and revision. This is a read, so acommit-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.
- Writes each remaining schedule back exactly as the platform holds it.
- 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.
Written back exactly as stored
deploy sends invocation_target as the manifest wrote it, so there is nothing here for pull to look up or
resolve — it writes back what list_schedules returns, field for field:
A schedule deployed by
blobhub-cli ≤ v0.4.0 pulls its invocation_target.target.blob back as the raw id, not
as an alias. Those releases resolved a manifest’s blob alias before writing, storing only the id, so pull writes
that same id back. Redeploying the original alias manifest once —
diff reports it as
update (invocation_target.target.blob, invocation_target.target.org) — stores the target the way the manifest
wrote it, and it round-trips through pull unchanged from then on.
The written entry always carries invocation_target_type, set to workflow, even where the manifest you started
from omitted it. It is the platform’s only accepted value, but a required field rather than a default the platform
infers — pull writes it explicitly every time.
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 only the scheduler blob
Writing a schedule back verbatim means there is nothing left to look up. pull never reads a schedule’s target
blob, revision or session — only the scheduler blob itself, through list_schedules. That makes it the same
reach as ls; the two commands differ only in what they do with what they list, not
in what they touch to get it.
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 sends the manifest’s target and workflow unchanged, but it resolves the target session — finding or
creating it — 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.

