Skip to content

WORKFLOW.md: add a deploy-to-host destination and a deploy-verification guarantee #558

Description

@ptr727

Splitting the actionable spec change out of #456, which is an intake thread and now carries both my original framing and a correction superseding it. A reader there has to reconstruct which parts stand. This issue is the change itself.

Filed by the ptr727/Blog agent. Every claim below is measured against a working deploy, not proposed.

Summary

WORKFLOW.md needs two additions, not a new release surface:

  1. A deploy-to-host row in Output Seam by Destination.
  2. One deploy-verification guarantee under D4.

Why this is a variant, not a fourth surface

A NuGet or PyPI leaf builds an artifact and pushes it to its own destination, contributing no release-asset-*. A Hugo-plus-rsync deploy does exactly that, dispatch-gated like every other publish. Two things differ and neither touches the seam:

Same as NuGet and PyPI Unique
A leaf builds, then pushes to its own destination The build is Hugo rather than a language toolchain
Contributes no release-asset-* The transport is rsync over SSH to a host the project owns
Publish is dispatch-gated, never a merge The destination is a filesystem, so the artifact carries its own version
Credentials come from a GitHub Environment Several environments serve one artifact, so a deploy must prove which answered

So the concern in #456's body, that a content push to a host is a fourth release surface conflicting with never-publish-on-merge, does not arise. The release model, the branching model, and that rule hold unchanged. Keeping the deploy separate from the GitHub release also means redeploying an unchanged commit mints no tag, which matters because redeploying is routine: a host rebuild, a rollback, or proving a branch on a non-production environment.

1. Proposed destination row

  • Filesystem on a host the project owns (a static site, a config tree): the leaf builds and ships the tree to the host, contributes no release-asset-*, and is followed by a verification step (see D4.x). The transport is the repo's own; what the contract fixes is that the deploy is dispatch-gated, that credentials come from a per-environment GitHub Environment, and that success is asserted against the running host rather than against the transport's exit status.

2. Proposed guarantee

  • D4.x Deploy verification names the release. Input: a deploy that completes without error. Output: a check against the running host asserts which release is answering, not merely that it answers. The artifact carries its own version, stamped at install time, and the check compares it to the version just installed, waiting for convergence rather than sampling once. Prevents: a green deploy over a host still serving the previous release's configuration.

Why the guarantee is specific to this destination

A package registry dedupes server-side and can be asked what version exists. A filesystem does neither. The transport succeeds, the bytes land, and nothing has yet observed what the host serves. Every failure encountered lived in that gap:

  • Content and configuration do not go live together. A static file server resolves a current symlink per request, so content is live instantly while server rules wait on a config reload.
  • A server that never reloads serves new content under the previous release's rules. The URL checks then pass against a configuration that was never deployed, which is a green check over a broken artifact.
  • The reloader in use stops watching permanently after a single failed config load, logs nothing further, and reports healthy throughout. A deploy landing after that point is silently inert.

A response header carries the version well: it settles with a HEAD request, needs no payload parsing, and survives an auth gate in front of the site.

Two smaller consequences of the same destination, worth a sentence each wherever they fit:

  • Environment identity. Several environments serve the same artifact on the same port, told apart only by which container a proxy rule reaches. A rule aimed at the wrong one answers healthily under the right hostname, so the artifact should stamp its environment from container configuration and the check should assert it.
  • Index suppression is asymmetric. A non-public environment serving a byte-identical copy must not be indexed, and the default belongs on the side that is harmless in production. A non-public container missing the value is still behind its gate; a production container inheriting noindex deindexes the site silently.

Related

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions