Skip to main content
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:
That is the correct answer — the revision really does hold nothing — but it is also the whole file. The single quotes are the writer’s own: it emits YAML that round-trips, quoting 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

  1. Loads the manifest and resolves the scheduler blob and revision. This is a read, so a commit-phase revision pulls fine.
  2. Pages list_schedules to completion.
  3. Sets aside every schedule the platform stored with no alias — they cannot be expressed as manifest entries.
  4. Reverses each remaining schedule’s stored target back into symbols.
  5. Writes a fresh document to the manifest path, entries sorted by alias.
Under --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