Skip to content

publish-release.yml's interface contract applies to publish-on-merge repos, but publish-plan-task.yml has no publish-on-merge path #1467

Closed

Description

@ptr727

Found resyncing ptr727/aiopurpleair against hub 8195c43. Related to #1116, which records the adjacent "these interface findings are accurate about the contract and misleading about the remedy" problem from the NxWitness resync. This one is a different defect: the contract is applied to a release trigger the task it names cannot serve.

The mismatch

spec/files.json declares .github/workflows/publish-release.yml at interface fidelity with:

"requiredJobKeys": ["plan", "validate", "publish"],
"requireTokensInJob": { "plan": ["publish-plan-task.yml"], ... },
"appliesTo": ["two-phase", "dispatch-only", "publish-on-merge"]

So a repository registered releaseTrigger: publish-on-merge is required to call publish-plan-task.yml. That task implements two-phase semantics and has no publish-on-merge path at all:

  • Its header states "A human PR merge never auto-publishes."
  • Its push arm publishes only when ref == main and the actor is ptr727-codegen[bot] or dependabot[bot].
  • A push to develop never publishes, on any actor.
  • grep -c 'publish-on-merge' .github/workflows/publish-plan-task.yml returns 0.

A publish-on-merge repository, by the registry's own definition of that value, publishes when a shipped-path change merges. Adopting the task the contract names is what stops it doing that. So the contract cannot be satisfied while the declared trigger remains true: satisfying it changes the repository's release behavior into a different declared trigger.

How it surfaced, and why it is worth a decision rather than a silent fix

aiopurpleair was the last repository still on publish-on-merge. Its own PR #26, opened 2026-07-10, already made this change and was never merged, while the same change landed in LanguageTags, Utilities and PlexCleaner. So in that repository the audit finding was correct and the registry field was the stale thing, and the resolution was to finish the migration (ptr727/aiopurpleair#92) and move the registry entry to two-phase.

That resolution happens to be right for aiopurpleair and does not generalize. It worked because publish-on-merge was never a deliberate choice there. For a repository that genuinely wants publish-on-merge, this contract has no satisfiable form.

The question

Either publish-on-merge should not appear in that appliesTo list, or publish-plan-task.yml needs a mode that serves it, or the value should be retired from registry/repos.schema.json if the fleet has decided no repository should use it. aiopurpleair was the only holder, so after its migration the enum value has no users, which makes retiring it the cheapest option and also the one most worth stating deliberately rather than reaching by attrition.

Registry follow-up owed regardless

registry/repos.json still records aiopurpleair as releaseTrigger: publish-on-merge. Now that ptr727/aiopurpleair#92 has merged to main, that field no longer describes the pipeline and should read two-phase.

Audit run stamp: audit run 2026-09-09T02:08:56Z | hub 8195c43.

Activity

  1. ptr727 commented on Sep 9, 2026

    @ptr727
    OwnerAuthor

    Settled on develop by #1476, merged as c23a4b8.

    Retirement was the resolution: publish-on-merge is gone from registry/repos.schema.json, spec/validate.py's vocabulary, the spec/scope-model.md selector table, and the publish-release.yml appliesTo list. The registry follow-up this issue said was owed regardless landed in the same change, since spec/validate.py would otherwise reject the entry: aiopurpleair now reads two-phase, which its own publisher satisfies after ptr727/aiopurpleair#92.

    Two things the work turned up, filed rather than folded in. #1478 records that MediaTools is registered two-phase and genuinely publishes on a human merge, with no plan job and no actor condition, so the field holder was migrated while a behavioral holder remains, and KiCadLibrary sits latently behind an unset PUBLISH_ON_MERGE variable. #1477 records the 33 defects four whole-unit review passes found in the merge-and-release procedure this change touched one sentence of, none of them introduced by it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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