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:
- A deploy-to-host row in
Output Seam by Destination.
- 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
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/Blogagent. Every claim below is measured against a working deploy, not proposed.Summary
WORKFLOW.mdneeds two additions, not a new release surface:Output Seam by Destination.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:release-asset-*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
2. Proposed guarantee
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:
currentsymlink per request, so content is live instantly while server rules wait on a config reload.A response header carries the version well: it settles with a
HEADrequest, 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:
noindexdeindexes the site silently.Related
🤖 Generated with Claude Code