Automated Publishing: Handling Unknown States

a close up of a computer screen with a blurry background

A publishing job that reports an error has not necessarily failed cleanly. It may have generated the intended files, uploaded part of them, changed a deployment target, or completed publication before losing its acknowledgement. Treating that outcome as a simple failure makes an immediate retry risky: the next run may duplicate a release, overwrite a different version, or obscure what happened first.

The practical design is to make unknown a first-class publishing state. When the workflow cannot establish whether a release completed, it should stop creating new publishing side effects, preserve the release record and affected set, check authoritative signals, and require an explicit resume, rollback, or manual-publication decision.

Model publication as more than pass or fail

A useful state model separates stages that are often collapsed into one job status:

  • Queued: the release has been accepted, but no publishing action has started.
  • Building: the workflow is generating output from a known input revision.
  • Artifact created: the expected output exists and has an identifiable manifest.
  • Delivery in progress: files or an artifact are being sent to the deployment system.
  • Confirmed: the system has enough evidence that the intended release reached its defined publication target.
  • Failed: the workflow has evidence that the intended side effect did not occur and the release can be retried under its defined rules.
  • Unknown: the workflow cannot determine whether one or more side effects occurred.
  • Reconciled: an authorized decision has resolved the unknown state into a controlled next action.

These states should not be inferred only from a process exit code. A successful Hugo build establishes that a build command produced output; it does not establish that deployment was acknowledged or that the public site serves that exact release. Hugo’s official documentation covers the CLI, configuration, hosting and deployment, and troubleshooting areas maintainers can use to define these stages. The provider-specific meaning of deployment acknowledgement still belongs in the workflow contract.

The central rule is simple: unknown blocks new publishing actions. It does not necessarily block inspection, evidence collection, or a read-only status check.

Before this boundary, run deterministic checks for drafts, stale output, and unintended content. The existing deterministic Hugo checks before publishing can serve as the pre-publish validation layer; unknown-state handling begins when an execution may already have caused external effects.

Make the stop control narrow and observable

A stop mechanism should answer three questions: what does it block, when is it checked, and how can an operator tell that it worked?

For example, a workflow might use a scoped pause record for one site or environment. Before starting a build, it checks whether a pause is active. Before uploading, promoting, or replacing a deployment target, it checks again. A final check before retry or promotion prevents a job that was already running when the pause was created from continuing into a new side effect.

This is a practical pattern, not a universal guarantee. One practitioner example recommends creating a stop mechanism before automating posting and illustrates a scoped pause-file approach; the article does not establish how such a control behaves with concurrent jobs or deployments that have already started (the example stop-control guidance). Your implementation needs explicit behavior for jobs that are queued, running, paused, or already in delivery.

A useful stop record should include the scope, creation time, creator or service identity, reason, affected environment, and condition for removal. The workflow should emit a visible event when it refuses to proceed. A silent flag is difficult to trust during an incident.

Stopping also needs a boundary. A pause may block new builds, uploads, promotions, or retries while leaving read-only status checks available. If it also prevents operators from retrieving logs or deployment status, it can preserve uncertainty rather than resolve it.

Preserve release identity before side effects

Retry safety depends on identifying the release independently from the job that attempted to publish it. A job identifier answers “which execution ran?” A release identifier answers “which content was this execution trying to publish?” Those are different questions.

Before delivery begins, create a release record containing at least:

  • a stable release identifier;
  • the source revision or content snapshot;
  • the Hugo build mode and relevant configuration identity;
  • the artifact location and content digest, if available;
  • the intended environment and deployment target;
  • the expected publication marker or version reference;
  • the affected pages, files, or content set;
  • timestamps for build, upload, promotion, and observed responses;
  • links or identifiers for provider-side deployment records;
  • the current state and reason for any transition.

The affected set matters because recovery rarely concerns an abstract “job.” It concerns the pages, assets, or deployment target that may have changed. A recovery sequence described in industry guidance begins by stopping further spread, identifying the affected set, correcting the public result, and repairing the workflow (the recovery sequence). That sequence is useful operational guidance, not independent evidence that the procedure prevents incidents.

A release marker can connect public output to a release record. In a hypothetical Hugo workflow, release R-104 might write a generated marker containing its identifier and source revision. That marker could be checked in the deployment system and, where appropriate, on a public diagnostic endpoint or page. It is useful only if it is generated from the same release record and maintained consistently; it should not be treated as proof that every file changed as intended.

Reconcile the unknown state before retrying

When delivery acknowledgement is missing, reconciliation should gather evidence without creating another release. Start with the strongest available signals and record what each one can and cannot establish:

  1. Build evidence: Does the expected artifact exist, and does its manifest match the release record?
  2. Delivery evidence: Does the hosting or deployment provider show an accepted, completed, failed, or still-running deployment for that release identity?
  3. Target evidence: Does the deployment target reference the expected artifact or revision?
  4. Public-state evidence: Does a controlled request to the public site expose the expected release marker or representative output?
  5. Scope evidence: Which files or pages may have changed, and which remain unverified?

Do not collapse these signals into one green or red status. A provider may report acceptance without public availability. A public page may show a new marker while another asset remains stale. A successful build may prove neither delivery nor public state.

The reconciliation record should state the evidence observed, its timestamp, the checks that were unavailable, and the decision authority. It should then choose one bounded path:

  • Resume the same release when the prior attempt is known not to have completed and the delivery operation is safe to repeat.
  • Promote or complete the existing release when the artifact exists and the deployment system can continue it without creating a competing release.
  • Rollback to a known release when the public result is incorrect or the deployment target is in a state the team has defined as unacceptable.
  • Publish manually when automation cannot establish the required state but the content is time-sensitive.
  • Escalate and keep paused when available signals conflict or the consequences of choosing incorrectly are high.

A GitHub issue about npm trusted publishing illustrates why integration behavior deserves explicit records: the report describes uncertainty about how automatic provenance interacts with a trusted-publishing workflow (the reported integration ambiguity). That issue does not establish the same behavior in Hugo, but it shows why an automation boundary can remain unclear even when individual steps appear successful.

Define retry authorization, not just retry logic

A retry should require more than a timeout. It should require a state transition authorized by a rule or reviewer. The authorization can reference the release identifier, evidence reviewed, selected action, and conditions that would invalidate the decision.

For example, a retry may be permitted only when the deployment provider confirms that no deployment for R-104 was accepted, the intended artifact remains unchanged, and the pause record is still active until the retry is admitted. A provider record showing that R-104 completed should route to verification or promotion rather than another upload.

Idempotency helps, but it is not a property to assume. It requires a stable operation key, defined duplicate-handling behavior, and a destination that honors that identity. If a provider does not offer those guarantees, the workflow should not claim that a repeated command is safe. It may instead create a new release only after the previous release has been reconciled and explicitly superseded.

Technical publication status should also remain separate from editorial approval. A release can be technically delivered but still require an editorial decision about whether the content should remain public. Conversely, editorial approval does not prove that the intended bytes reached the public target.

The practical decision rule is narrow: when a publishing workflow cannot establish what happened, stop new side effects, preserve the release identity and affected set, collect read-only evidence, and require reconciliation before resuming. A pause may delay a legitimate release, and manual review adds operational work. An uncontrolled retry can make the original state harder to recover or even harder to determine.