A whole-unit canonical pass over ## D4: Release and Publish in .agents/skills/workflow-ci-contract/references/d-guarantees.md found six defects beyond the one #1212 fixes. Each is a condensation that drops the clause carrying the item's meaning, so a reader holding only the catalog reaches a different answer than WORKFLOW.md gives.
D4.4 asserts and denies the same output in one sentence
an unchanged version re-pushes nothing ... and Docker always re-pushes by design
WORKFLOW.md D4.4 resolves this and the condensation drops both resolvers: the NuGet and PyPI publish steps "are not statically gated on existence. They run and the server dedupes", and Docker's re-push happens "independently of the release-create skip". A unit-only reader gets a flat contradiction with nothing to settle it.
D4.4 drops the audit disposition for the reader most likely to need it
The source adds "which is why a dispatch-only publisher records this item's skip leg as unreachable rather than failed". This hub and every source-only repository in the fleet are dispatch-only, so the omission lands hardest on the common case. Adjacent to #1204, which is the same seam from the scenario side.
D4.5 names a per-target skip the publish path does not produce
so a disabled or unchanged target, skipped rather than failed, still lets the release be cut and the image pushed
"Unchanged" has no mechanism here. publish-plan-task.yml carries no paths filter, the changes job lives in the pull request workflow under D1.1, and the release task gates leaves on inputs.enable_* alone. Nothing in the publish path skips a target for being unchanged. This is carried verbatim from WORKFLOW.md D4.5, so the fix belongs at that source, and it would hold only for a downstream publisher that added a per-target filter, which nothing documents.
D4.3 may name the wrong failing step
or the release-create step fails on unmatched files
With expect_release_assets left true and no release-asset-* produced, the download step runs first, under if: ${{ inputs.expect_release_assets }} with pattern: release-asset-${{ inputs.branch }}-*, and actions/download-artifact errors on an empty match before the create step's fail_on_unmatched_files is ever evaluated. The pinned action's empty-pattern behavior was not confirmed offline, so this is flagged rather than asserted. WORKFLOW.md D4.3 carries the same claim, so the fix, if the behavior confirms it, belongs there.
D4.1 drops the source-only trigger case
It lists three publish triggers and omits "A source-only repo publishes on dispatch only", so a source-only repository, this hub included, reading only this unit expects a push and a schedule leg it does not have.
D4.6 drops its input scope
The source scopes it to "a deploy to a filesystem on a host the project owns". Without that, a unit-only reader cannot tell the item is deploy-repository-only rather than fleet-wide. The file's preamble does not repair this: its "an item names an input only where the guarantee applies to a particular trigger or state" describes WORKFLOW.md's labelled-clause convention, not this catalog, which is the defect #1205 covers.
Verified correct, listed so a later pass need not redo them
D4.2's target_commitish claim (build-release-task.yml:354 pins needs.get-version.outputs.GitCommitId), D4.3's prerelease rule and its no-file-target caller shape, D4.5's needs: topology and the !failure() && !cancelled() guards on both github-release and build-docker, and the publish-<target> job sitting in the publisher after the release task.
Related
Found while fixing the D4.5 recovery clause for #1212, raised by CodeRabbit on #1203. Related to #1205 (the same file's preamble), #1206, #1207, #1208 and #1201.
A whole-unit canonical pass over
## D4: Release and Publishin.agents/skills/workflow-ci-contract/references/d-guarantees.mdfound six defects beyond the one #1212 fixes. Each is a condensation that drops the clause carrying the item's meaning, so a reader holding only the catalog reaches a different answer thanWORKFLOW.mdgives.D4.4 asserts and denies the same output in one sentence
WORKFLOW.mdD4.4 resolves this and the condensation drops both resolvers: the NuGet and PyPI publish steps "are not statically gated on existence. They run and the server dedupes", and Docker's re-push happens "independently of the release-create skip". A unit-only reader gets a flat contradiction with nothing to settle it.D4.4 drops the audit disposition for the reader most likely to need it
The source adds "which is why a dispatch-only publisher records this item's skip leg as unreachable rather than failed". This hub and every source-only repository in the fleet are dispatch-only, so the omission lands hardest on the common case. Adjacent to #1204, which is the same seam from the scenario side.
D4.5 names a per-target skip the publish path does not produce
"Unchanged" has no mechanism here.
publish-plan-task.ymlcarries no paths filter, thechangesjob lives in the pull request workflow under D1.1, and the release task gates leaves oninputs.enable_*alone. Nothing in the publish path skips a target for being unchanged. This is carried verbatim fromWORKFLOW.mdD4.5, so the fix belongs at that source, and it would hold only for a downstream publisher that added a per-target filter, which nothing documents.D4.3 may name the wrong failing step
With
expect_release_assetsleft true and norelease-asset-*produced, the download step runs first, underif: ${{ inputs.expect_release_assets }}withpattern: release-asset-${{ inputs.branch }}-*, andactions/download-artifacterrors on an empty match before the create step'sfail_on_unmatched_filesis ever evaluated. The pinned action's empty-pattern behavior was not confirmed offline, so this is flagged rather than asserted.WORKFLOW.mdD4.3 carries the same claim, so the fix, if the behavior confirms it, belongs there.D4.1 drops the source-only trigger case
It lists three publish triggers and omits "A source-only repo publishes on dispatch only", so a source-only repository, this hub included, reading only this unit expects a push and a schedule leg it does not have.
D4.6 drops its input scope
The source scopes it to "a deploy to a filesystem on a host the project owns". Without that, a unit-only reader cannot tell the item is deploy-repository-only rather than fleet-wide. The file's preamble does not repair this: its "an item names an input only where the guarantee applies to a particular trigger or state" describes
WORKFLOW.md's labelled-clause convention, not this catalog, which is the defect #1205 covers.Verified correct, listed so a later pass need not redo them
D4.2's
target_commitishclaim (build-release-task.yml:354pinsneeds.get-version.outputs.GitCommitId), D4.3'sprereleaserule and its no-file-target caller shape, D4.5'sneeds:topology and the!failure() && !cancelled()guards on bothgithub-releaseandbuild-docker, and thepublish-<target>job sitting in the publisher after the release task.Related
Found while fixing the D4.5 recovery clause for #1212, raised by CodeRabbit on #1203. Related to #1205 (the same file's preamble), #1206, #1207, #1208 and #1201.