Skip to content

Host the Type-Specific Tasks and Retire the Date Badge - #761

Merged
ptr727 merged 25 commits into
developfrom
feature/type-specific-tasks
Aug 16, 2026
Merged

Host the Type-Specific Tasks and Retire the Date Badge#761
ptr727 merged 25 commits into
developfrom
feature/type-specific-tasks

Conversation

@ptr727

@ptr727 ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner

What

Stage 5 of the hub-hosted reusable-workflow rollout (docs/reusable-workflows.md
"Rollout"). Hosts four more reusable tasks in the hub and retires one:

  • publish-docker-readme-task.yml: the transform-run/transform-artifact string
    inputs become a docker-readme-transform hook, with a hub default that publishes
    Docker/README.md if present else README.md as-is. Explicit DOCKER_HUB_USERNAME/
    DOCKER_HUB_ACCESS_TOKEN secrets (the names spec/secrets.json already declares).
    A branch input keeps the readme push main-only inside the task itself.
  • check-upstream-version-task.yml: the resolver-command string input becomes a
    required resolve-upstream hook. Adds an auto-merge boolean input (default true);
    false prefixes the bump PR's head so no merge-bot rule matches it, routing the bump
    to a human. ESPHome-NonRoot's second tracker, check-upstream-dependency.yml, folds
    into this same task with auto-merge: false on adoption.
  • deploy-site-task.yml: the three path-conventioned scripts
    (deploy/make-release.sh, deploy/prune-releases.sh, checks/check-live-urls.sh)
    fold into one required deploy hook, invoked three times with a mode input
    (build, prune, verify), while the environment binding and the atomic
    upload-then-flip sequence stay hub-owned. deploy-site.yml stays a per-repo caller,
    documented as a stub in docs/reusable-workflows.md.
  • run-codegen-pull-request-task.yml: a required codegen hook for the generation
    step; the App-token, setup, format, and PR-opening machinery stay hub-owned since
    both current carriers (LanguageTags, NxWitness) run it identically.
    NINJA_API_KEY becomes an optional secret, since neither carrier maps it today.
  • build-datebadge-task.yml is retired rather than hosted: TODO.md already tracks
    deleting the retired byob.yarr.is badge from its remaining carrier (KiCadLibrary).

Retires the corresponding catalog snippets and updates the catalog README, adds
retire gap dispositions in spec/divergences.json naming carriers, updates
spec/files.json's deploy-site.yml/deploy-site-task.yml entries for the new
hub-hosted contract (dropping secrets: inherit, requiring the mapped
DEPLOY_SSH_PRIVATE_KEY secret name instead), and adds check_interface self-test
cases in spec/audit.py for every new stub contract. Fixes prose in WORKFLOW.md
and the operational-vs-release-workflow skill that named the retired snippets, the
old secrets: inherit shape, or the retired date-badge job. Adds "Adopting the
Type-Specific Tasks" to docs/reusable-workflows.md with the caller uses: lines
and hook names (pins as placeholders), ticks stage 5's hub PR item, and lists
adoption checkboxes per carrier.

Rebased onto develop after #758 (the fleet's CRLF -> LF line-ending flip), so every
new/edited file already follows the new LF default.

Design choices where the brief left room

  • job.workflow_repository/job.workflow_sha (not github.job_workflow_sha, which
    does not exist) resolve the hub checkout in publish-docker-readme-task.yml's
    default-hook path. actionlint's context schema has not caught up to these
    documented GitHub Actions job context properties yet, so a scoped
    .github/actionlint.yaml ignore entry covers the false finding until it does.
  • The deploy hook is a single composite action taking a mode input
    (build/prune/verify) rather than three separate hooks, so a site repo owns
    the whole per-repo half in one file. Blog's live copy already carries more than
    three clean scripts (its own install-hugo action, a git-mtime restore step,
    PANGOLIN tokens for its staging auth check), which argues for hook flexibility
    over a path convention, not against it.
  • The one secret that crosses the deploy-site-task.yml reusable-workflow boundary,
    DEPLOY_SSH_PRIVATE_KEY, is a GitHub Environment secret. The caller's own deploy
    job binds the same environment: the task binds, which is what lets that secret
    resolve at the call site for explicit mapping instead of secrets: inherit
    (forbidden here, and unusable cross-repository on a personal account regardless).
  • ESPHome-NonRoot's second tracker folds into check-upstream-version-task.yml by
    treating its apt-package snapshot as a one-key name -> version object
    ({"docker_base_packages": "<sorted, comma-joined list>"}). This loses today's
    bespoke "packages added/removed" title wording in favor of the shared task's
    generic title, a documented tradeoff.

Could not prove

  • The DEPLOY_SSH_PRIVATE_KEY environment-secret handoff across a cross-repository
    uses: (the caller job's environment: binding resolving it for explicit mapping)
    is correct per GitHub's documented behavior, but only Blog's first live deploy run
    after adoption proves it. Tracked as a proof item in the rollout tracker.
  • job.workflow_repository/job.workflow_sha resolving to the hub at the caller's
    pinned commit is documented GitHub behavior, unverified by a live run in this PR
    (the hub has no downstream caller of its own docker-readme task to trigger it).

Conflicts with sibling work

None encountered. Stage 4 (build-release-task.yml etc.) has not merged as of this
PR, so the operational-vs-release-workflow skill's "until that phase ships the list
stays per repo" sentence is left as-is per the brief's instruction.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 16, 2026 13:17
ptr727 added a commit that referenced this pull request Aug 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Stages “Type-Specific Tasks” reusable-workflow rollout by moving additional task workflows into the hub (via hook-based extensibility), updating the documented/audited contracts for downstream caller stubs, and retiring the date-badge task and related catalog snippets.

Changes:

  • Add hub-hosted reusable workflows for Docker Hub readme publishing and update existing hub tasks (upstream-version tracker, deploy-site, codegen) to use required hooks and updated contracts.
  • Retire date-badge and multiple catalog snippet workflows; update rollout/adoption docs and skill references accordingly.
  • Update spec/audit selftests and divergence dispositions to reflect the new hub-hosted vs retired workflow surfaces.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
WORKFLOW.md Updates workflow guarantees/docs to reflect hub-hosted type-specific tasks and date-badge retirement.
TODO.md Updates backlog items and references to point at hub-hosted tasks and retired badge work.
spec/files.json Updates interface contract expectations for deploy-site caller now that task is hub-hosted (explicit secret mapping).
spec/divergences.json Adds retire dispositions for workflows moving to hub-hosted tasks and for date-badge retirement.
spec/audit.py Adds check_interface selftest cases for new/updated caller-stub contracts.
scripts/prose_lint.py Expands HUB_HOSTED allowlist to prevent dead-path linting for hub-hosted/retired workflow paths.
docs/reusable-workflows.md Documents “Adopting the Type-Specific Tasks” including stubs, hooks, and rollout proof items.
catalog/snippets/workflows/run-periodic-codegen-pull-request.yml Removes snippet (retired/migrated to hub-hosted contract).
catalog/snippets/workflows/README.md Updates catalog README to reflect hub-hosted task set and snippet sequencing rules.
catalog/snippets/workflows/publish-docker-readme-task.yml Removes snippet (task now hub-hosted).
catalog/snippets/workflows/deploy-site.yml Removes snippet (caller now documented as stub; task hub-hosted).
catalog/snippets/workflows/build-datebadge-task.yml Removes snippet (task retired).
.github/workflows/run-codegen-pull-request-task.yml Updates codegen task to require a repo-owned codegen hook and makes NINJA_API_KEY optional.
.github/workflows/publish-docker-readme-task.yml Adds new hub-hosted Docker Hub readme publish task with hook/default-hook behavior.
.github/workflows/deploy-site-task.yml Refactors deploy-site task to require a single deploy hook (build/prune/verify modes) while keeping hub-owned upload/flip.
.github/workflows/check-upstream-version-task.yml Replaces resolver-command input with required resolve-upstream hook and adds auto-merge control.
.github/actions/docker-readme-transform-default/action.yml Adds hub default implementation for docker readme transform hook.
.github/actionlint.yaml Adds scoped actionlint ignores for job context properties used by publish-docker-readme default-hook checkout.
.claude-plugin/fleet-skills/skills/operational-vs-release-workflow/references/release-publish-mechanics.md Updates generated skill reference docs for new hub-hosted task behavior/retirements.
.claude-plugin/fleet-skills/.source-digest Updates generated plugin digest for the regenerated skill payload.
.agents/skills/operational-vs-release-workflow/references/release-publish-mechanics.md Updates source skill reference docs to match new hub-hosted task behavior/retirements.
Suppressed comments (1)

.github/workflows/run-codegen-pull-request-task.yml:64

  • NINJA_API_KEY is declared as an optional workflow_call secret and the comment says it’s consumed by the codegen hook, but the hook step doesn’t pass it via env/inputs. Composite actions don’t get the secrets context implicitly, so the hook won’t be able to read the key even when the caller maps it.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/publish-docker-readme-task.yml
Comment thread .github/workflows/publish-docker-readme-task.yml
Copilot AI review requested due to automatic review settings August 16, 2026 13:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.

Suppressed comments (2)

.github/workflows/run-codegen-pull-request-task.yml:65

  • NINJA_API_KEY is declared as an optional workflow_call secret, but it is never passed into the required ./.github/actions/codegen hook. Composite actions generally can’t read secrets.* directly, so the hook won’t be able to use the key even when a caller maps it.
    TODO.md:62
  • This line says the task is hub-hosted, but the [workflows] reference points to ./catalog/snippets/workflows/ (where the snippet was removed). Link directly to the hub-hosted workflow file so the reference doesn’t send readers to the wrong location.
  - **Checked** - `develop` on 2026-08-08, where `repo-config/configure.sh` sets every other repository setting and carries no `description` handling, and the hub-hosted [`publish-docker-readme-task.yml`][workflows] pushes `github.event.repository.description` to Docker Hub.

Copilot AI review requested due to automatic review settings August 16, 2026 13:29
@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering two suppressed (low-confidence) Copilot findings from the review on 0ad91d7, since a suppressed finding opens no thread to resolve.

NINJA_API_KEY is declared as an optional workflow_call secret and the comment says it's consumed by the codegen hook, but the hook step doesn't pass it via env/inputs. Composite actions don't get the secrets context implicitly, so the hook won't be able to read the key even when the caller maps it.

Correct, fixed in fe0ab34. GitHub Actions restricts the secrets context from composite actions for security, so the hub task now passes ${{ secrets.NINJA_API_KEY }} to the codegen hook as an explicit ninja-api-key input, and docs/reusable-workflows.md's codegen adoption section documents the contract.

This line says the task is hub-hosted, but the [workflows] reference points to ./catalog/snippets/workflows/ (where the snippet was removed). Link directly to the hub-hosted workflow file so the reference doesn't send readers to the wrong location.

Correct, fixed in fe0ab34. Added a [publish-docker-readme-task] reference pointing at .github/workflows/publish-docker-readme-task.yml and repointed the TODO.md mention at it, leaving the shared [workflows] alias (still valid, pointing at the directory) for its other use.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (5)

.github/workflows/check-upstream-version-task.yml:6

  • The header comment says the resolve-upstream hook "prints" versions, but the workflow actually reads them from steps.resolve-upstream.outputs.versions (it does not capture stdout). This can mislead adopters implementing the hook; it should explicitly document that the hook must set an action output named versions (single-line JSON) via $GITHUB_OUTPUT.
    docs/reusable-workflows.md:251
  • This section introduces the docker-readme-transform hook but doesn’t state the required contract (the workflow consumes readme-filepath via action outputs). Without documenting the required output name, adopters may implement a hook that only writes to stdout and the publish step will fail with an empty path.
**Docker Hub readme.** A repo publishing an image replaces its `publish-docker-readme-task.yml` job body with a caller stub reaching the hub task. A `docker-readme-transform` hook, `.github/actions/docker-readme-transform/action.yml`, is needed only to render the readme first or to override the hub default, which publishes `Docker/README.md` if present else `README.md` as-is.

docs/reusable-workflows.md:267

  • The adoption text says the resolve-upstream hook "prints" versions, but the hub task reads versions from an action output (steps.resolve-upstream.outputs.versions). The docs should specify that the hook must set an output named versions (single-line JSON) via $GITHUB_OUTPUT.
**Upstream-version tracker.** A repo tracking an upstream release replaces `check-upstream-version-task.yml`'s job body with a caller stub, and carries a required `resolve-upstream` hook, `.github/actions/resolve-upstream/action.yml`, printing the upstream version(s) as a JSON object of name -> version.

.github/workflows/publish-docker-readme-task.yml:9

  • The comment describes the docker-readme-transform hook behavior but doesn’t state the concrete interface the workflow relies on. Since the workflow consumes steps.<hook>.outputs.readme-filepath, the comment should explicitly mention the required readme-filepath output (set via $GITHUB_OUTPUT) to avoid adopters implementing a hook that only writes to stdout.
# A docker-readme-transform hook resolves which file to push, defaulting to Docker/README.md when present, else README.md as-is.
# A repo needs a hook only to override that choice or to render the file first, an m4 template, for example.

.agents/skills/operational-vs-release-workflow/references/release-publish-mechanics.md:130

  • This guidance says the resolve-upstream hook "prints" versions, but the hub task consumes an action output (steps.resolve-upstream.outputs.versions). Clarify in the skill reference that the hook must set an output named versions (single-line JSON) via $GITHUB_OUTPUT (and regenerate the .claude-plugin copy accordingly).
A repo wrapping an upstream release uses the hub-hosted `check-upstream-version-task.yml`: a
required `resolve-upstream` hook prints the upstream version(s) as a **JSON object of
`name -> version`**, written to a committed state file at the **repo root beside `version.json`**
(default `upstream-version.json`, since it is a build-input version source, not GitHub-platform
config, so it does not belong under `.github/`), and opens a rolling App-signed bump PR per branch

ptr727 added 5 commits August 16, 2026 06:38
Stage 5 of the hub-hosted reusable-workflow rollout: hosts
publish-docker-readme-task.yml (docker-readme-transform hook, with a hub
default), check-upstream-version-task.yml (resolve-upstream hook, an
auto-merge input), deploy-site-task.yml (deploy hook covering build,
prune, and verify), and run-codegen-pull-request-task.yml (codegen
hook). build-datebadge-task.yml is retired outright rather than hosted,
since the badge service it depended on is already tracked as retired.

Retires the corresponding catalog snippets, updates the catalog README,
adds retire gap dispositions in spec/divergences.json naming carriers,
updates spec/files.json's deploy-site.yml/deploy-site-task.yml entries
for the new hub-hosted contract, adds check_interface self-test cases
in spec/audit.py, and fixes prose across WORKFLOW.md and the
operational-vs-release-workflow skill that named the retired snippets
or the old secrets: inherit shape. Adds the Adopting the Type-Specific
Tasks section to docs/reusable-workflows.md and ticks the stage 5
tracker entries.
Copilot review on 0ad91d7: publish-docker-readme-task.yml's get-repos
and publish-readme jobs check out code with no declared permissions,
which fails against a caller that sets permissions: {} per the fleet
convention. Grants contents: read on both, matching deploy-site-task.yml's
existing precedent.

run-codegen-pull-request-task.yml declared NINJA_API_KEY as an optional
secret but never passed it to the codegen hook. A composite action
never sees the secrets context directly (GitHub Actions restricts it
for security), so the hub task now passes it as a ninja-api-key input,
documented in docs/reusable-workflows.md's codegen adoption section.

Also points TODO.md's publish-docker-readme-task.yml mention at the
hub-hosted file directly instead of the retired catalog snippet path.
Copilot review on fe0ab34: the header comments and adoption doc said a
resolve-upstream or docker-readme-transform hook prints its result,
which could be read as writing to stdout. Both hooks actually
communicate through a named composite-action step output
(versions, readme-filepath) that the hub task reads via
steps.<hook>.outputs.*, so an adopter following prints literally would
implement a hook the task can never read. Names the output explicitly
everywhere the hook contract is described: both hub task files, the
Adopting the Type-Specific Tasks doc section, and the
operational-vs-release-workflow skill reference.
Copilot AI review requested due to automatic review settings August 16, 2026 13:39
@ptr727
ptr727 force-pushed the feature/type-specific-tasks branch from fe0ab34 to 6d38066 Compare August 16, 2026 13:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (1)

spec/files.json:35

  • The deploy-site.yml interface contract doesn’t currently require an environment: binding on the deploy job. That binding is essential for the cross-repo deploy adoption described in docs/reusable-workflows.md (the DEPLOY_SSH_PRIVATE_KEY is intended to resolve from the GitHub Environment store at the call site). As-is, the audit could accept a caller stub that maps DEPLOY_SSH_PRIVATE_KEY but forgets environment:, and the deploy would fail at runtime.
        { "path": ".github/workflows/deploy-site.yml", "fidelity": "interface", "contract": { "requiredJobKeys": ["assert-ref", "validate", "deploy"], "requireTokensInJob": { "deploy": ["deploy-site-task.yml", "DEPLOY_SSH_PRIVATE_KEY"] } }, "intentRef": "docs/reusable-workflows.md#adopting-the-type-specific-tasks", "appliesTo": ["hugo"] },

Copilot review on 6d38066: the deploy-site.yml interface contract
required the crossing DEPLOY_SSH_PRIVATE_KEY secret but not the
environment: binding on the caller's deploy job that makes it resolve
from the GitHub Environment store. A caller stub could map the secret
and still fail at runtime by omitting the binding, and the audit would
not catch it. Adds environment: to the required tokens and a
self-test case for the omission.
Copilot AI review requested due to automatic review settings August 16, 2026 13:46
@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering the remaining suppressed (low-confidence) Copilot findings, since a suppressed finding opens no thread to resolve.

From the review on fe0ab34 (5 findings, all the same shape: the hook contract said a hook "prints" its result, which reads as stdout rather than a step output):

The header comment says the resolve-upstream hook "prints" versions, but the workflow actually reads them from steps.resolve-upstream.outputs.versions ... it should explicitly document that the hook must set an action output named versions ...

This section introduces the docker-readme-transform hook but doesn't state the required contract (the workflow consumes readme-filepath via action outputs) ...

The adoption text says the resolve-upstream hook "prints" versions ... The docs should specify that the hook must set an output named versions ...

The comment describes the docker-readme-transform hook behavior but doesn't state the concrete interface the workflow relies on ... should explicitly mention the required readme-filepath output ...

This guidance says the resolve-upstream hook "prints" versions ... Clarify in the skill reference that the hook must set an output named versions ... (and regenerate the .claude-plugin copy accordingly).

Correct, fixed in 8d17ed6. Every place describing the two hooks now names the step output by name (versions, readme-filepath) instead of "prints", across both hub task files, the adoption doc section, and the skill reference, with .claude-plugin/ regenerated.

From the review on 6d38066:

The deploy-site.yml interface contract doesn't currently require an environment: binding on the deploy job. That binding is essential for the cross-repo deploy adoption ... the audit could accept a caller stub that maps DEPLOY_SSH_PRIVATE_KEY but forgets environment:, and the deploy would fail at runtime.

Correct, fixed in fe59f1a. Added environment: to deploy-site.yml's required tokens in spec/files.json, plus a check_interface self-test case for the omission.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.github/workflows/publish-docker-readme-task.yml:145

  • actions/checkout is using ${{ job.workflow_repository }} / ${{ job.workflow_sha }} to resolve the hub revision. These context fields are not reliably available in reusable workflows (and the actionlint ignore only silences lint, not runtime), so the default-hook path can fail at runtime for callers that don’t carry a docker-readme-transform hook.
        if: ${{ hashFiles('.github/actions/docker-readme-transform/action.yml') == '' }}
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          repository: ${{ job.workflow_repository }}
          ref: ${{ job.workflow_sha }}

@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering a suppressed (low-confidence) finding from the latest round.

actions/checkout is using ${{ job.workflow_repository }} / ${{ job.workflow_sha }} to resolve the hub revision. These context fields are not reliably available in reusable workflows (and the actionlint ignore only silences lint, not runtime), so the default-hook path can fail at runtime for callers that don't carry a docker-readme-transform hook.

Disproved. job.workflow_sha, job.workflow_repository, job.workflow_ref, and job.workflow_file_path are documented GitHub Actions context properties (GitHub Docs, "Contexts", the job context table), and the job context is listed as available in every jobs.<job_id>.steps.* key. The one documented unavailability is GitHub Enterprise Server, which does not apply here (github.com). Their stated purpose is exactly this pattern: naming the exact commit of the reusable workflow file that defines the current job, which for publish-docker-readme-task.yml is the commit the caller's uses: line pinned, so a caller can check out the hub at that same commit.

The actionlint ignore entry in .github/actionlint.yaml exists only because actionlint's context schema has not caught up to these fields yet (a documented, scoped tooling gap, not a runtime concern), which the file's own header comment says.

This is genuinely unverified by a live run in this PR, since the hub has no downstream caller of its own docker-readme task to trigger it, which the tracker in docs/reusable-workflows.md "Stage 5" already lists as a proof item to close on Blog's/VSCode-Server-DotNetCore's adoption.

Copilot AI review requested due to automatic review settings August 16, 2026 13:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

docs/reusable-workflows.md:312

  • This paragraph says the hub task passes NINJA_API_KEY to the hook as a ninja-api-key input and that existing adopters’ hooks won’t declare that input. That combination would make the workflow fail with an unexpected input error. Update the docs to match the workflow contract (e.g., passing the optional key via env: NINJA_API_KEY, or requiring all hooks to declare the input).
The hub task passes `NINJA_API_KEY` to the hook as a `ninja-api-key` input, since a composite action never sees the `secrets` context directly and a hook whose generator calls a metered API needs the key handed to it explicitly. Neither LanguageTags nor NxWitness calls a metered API from its generator today, so neither maps `NINJA_API_KEY` and neither hook declares the input. Both add `dotnet husky install` ahead of the `dotnet csharpier format` step in their present copies, which the hub task carries verbatim, so a hook needs only the generator invocation itself (`dotnet run --project ...`).

Comment thread .github/workflows/run-codegen-pull-request-task.yml Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

scripts/prose_lint.py:356

  • HUB_HOSTED includes run-periodic-codegen-pull-request.yml, but this workflow is described in this PR as a per-repo caller stub (not hub-hosted or retired). Including it here suppresses dead-path findings for downstream repos that should continue carrying the scheduler.
        ".github/workflows/run-codegen-pull-request-task.yml",
        ".github/workflows/run-periodic-codegen-pull-request.yml",
        ".github/workflows/build-datebadge-task.yml",

scripts/prose_lint.py:338

  • This comment says run-periodic-codegen-pull-request.yml is “retired outright with no hub replacement”, but the rollout doc in this PR explicitly says the scheduler stays as a per-repo caller stub. Keeping it listed here would exempt stale references to a file that should still exist downstream.

This issue also appears on line 354 of the same file.

# Paths retired per a `retire` disposition in `spec/divergences.json`, per GOVERNANCE.md "Hub-Hosted Tooling".
# Most are hub-hosted, so a mention names the hub's copy rather than a file this tree lost.
# A minority are retired outright with no hub replacement (`run-periodic-codegen-pull-request.yml`, `build-datebadge-task.yml`): the mention still names a real, intentional deletion rather than a stale reference, so the same exemption applies.

Comment thread spec/divergences.json Outdated
Copilot review on 86630e9: spec/divergences.json marked
run-periodic-codegen-pull-request.yml retire, but the rollout doc
already says it stays a per-repo caller stub with only its uses: line
changing, the same shape as merge-bot-pull-request.yml and
deploy-site.yml. Retire means the hub hosts the file and a downstream
copy is deleted, which is wrong here and would have told a carrier to
delete a file it still needs. Removed the gap entry and the matching
HUB_HOSTED dead-path exemption, since a persisting caller stub needs
neither.
Copilot AI review requested due to automatic review settings August 16, 2026 15:56
@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering the suppressed (low-confidence) findings from the review on 86630e9, the same underlying issue as the open thread on spec/divergences.json.

HUB_HOSTED includes run-periodic-codegen-pull-request.yml, but this workflow is described in this PR as a per-repo caller stub (not hub-hosted or retired). Including it here suppresses dead-path findings for downstream repos that should continue carrying the scheduler.

This comment says run-periodic-codegen-pull-request.yml is "retired outright with no hub replacement", but the rollout doc in this PR explicitly says the scheduler stays as a per-repo caller stub. Keeping it listed here would exempt stale references to a file that should still exist downstream.

Both correct, fixed in 17fc7e0. Removed run-periodic-codegen-pull-request.yml from HUB_HOSTED and its retire gap entry in spec/divergences.json, and clarified the comment. It stays a per-repo caller stub, the same shape as merge-bot-pull-request.yml and deploy-site.yml, so it needs neither a retire disposition nor a dead-path exemption.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (2)

.github/workflows/publish-docker-readme-task.yml:121

  • After making get-repos always run, publish-readme should be explicitly main-only to preserve the intended behavior (no Docker Hub push from develop/other branches).
  publish-readme:
    name: Publish Docker Hub readme job
    runs-on: ubuntu-latest
    needs: get-repos
    permissions:

.github/workflows/publish-docker-readme-task.yml:56

  • get-repos is skipped when inputs.branch != 'main', which makes needs.get-repos.outputs.repositories undefined/empty for publish-readme's matrix. That commonly breaks fromJSON(...) evaluation (instead of cleanly no-op’ing on non-main). Keep get-repos running so it always produces a valid repositories output, and put the main-only gate on the publishing job instead.

This issue also appears on line 117 of the same file.

  get-repos:
    name: Get repository list job
    runs-on: ubuntu-latest
    if: ${{ inputs.branch == 'main' }}
    permissions:
      contents: read

ptr727 added 2 commits August 16, 2026 09:03
Copilot review on 17fc7e0: publish-readme relies on the needs:
skip-cascade from get-repos's own branch gate to stay main-only, which
is correct today but leaves the guarantee implicit. States the same
if: inputs.branch == 'main' condition on publish-readme directly, so
the main-only guarantee does not depend on the needs relationship
alone.
…fic-tasks

# Conflicts:
#	.github/actionlint.yaml
#	TODO.md
#	docs/reusable-workflows.md
#	scripts/prose_lint.py
#	spec/divergences.json
Copilot AI review requested due to automatic review settings August 16, 2026 16:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (2)

spec/files.json:34

  • In this interface contract, requiring the token environment: won’t actually prove the deploy job binds a GitHub Environment, because the job’s with: block also contains environment:. If a caller accidentally drops the job-level environment: binding, the contract can still pass due to the with.environment line. Tighten the token to match the job-level binding (indent-sensitive) so the audit catches the real failure mode your self-test describes.
        { "path": ".github/workflows/deploy-site.yml", "fidelity": "interface", "contract": { "requiredJobKeys": ["assert-ref", "validate", "deploy"], "requireTokensInJob": { "deploy": ["deploy-site-task.yml", "environment:", "contents: read", "DEPLOY_SSH_PRIVATE_KEY"] } }, "intentRef": "docs/reusable-workflows.md#adopting-the-type-specific-tasks", "appliesTo": ["hugo"] },

spec/audit.py:2270

  • The self-test case for “missing the environment binding” (deploy_stub without the job-level environment:) won’t be reliably detected if the contract only requires the substring environment:, because the deploy job still contains with:\n environment:. Require an indent-anchored token so the check specifically proves the job-level environment binding is present.
                "environment:",

Copilot review on ecdbd7a: requiring the bare substring environment:
in deploy-site.yml's contract does not prove the deploy job binds a
GitHub Environment, since the job's with: block also contains an
environment: input for the task. A caller stub that drops only the
job-level binding while keeping the with: input would still pass.
Anchors the token to \n    environment: (4-space indent), which the
with:-nested input's 6-space indent cannot satisfy, and rewrites the
self-test case to remove only the job-level line so it actually
exercises this ambiguity instead of coincidentally clearing both.
Copilot AI review requested due to automatic review settings August 16, 2026 16:19
@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering the suppressed (low-confidence) findings from the review on ecdbd7a.

In this interface contract, requiring the token environment: won't actually prove the deploy job binds a GitHub Environment, because the job's with: block also contains environment:. If a caller accidentally drops the job-level environment: binding, the contract can still pass due to the with.environment line.

The self-test case for "missing the environment binding" (deploy_stub without the job-level environment:) won't be reliably detected if the contract only requires the substring environment:, because the deploy job still contains with:\n environment:. Require an indent-anchored token so the check specifically proves the job-level environment binding is present.

Both correct, fixed in cd78cae. Anchored the token to \n environment: (the job-level line's 4-space indent), which the with:-nested input's 6-space indent cannot satisfy. Also rewrote the self-test case: it previously removed the job-level line via a plain string replace that, on inspection, also happened to corrupt the with: line's own occurrence (the 4-space search string is itself a substring of the 6-space line), so it passed without actually exercising the ambiguity these findings describe. It now removes only the job-level line, leaving the with: input untouched.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (2)

.github/workflows/deploy-site-task.yml:10

  • The header comment says the deploy hook is invoked only with a mode input, but the workflow actually passes additional with: inputs (bundle-path, release-id, and later environment). Since composite actions reject undeclared inputs, the comment should reflect the full hook input contract so adopters implement a compatible .github/actions/deploy/action.yml.
    docs/reusable-workflows.md:453
  • The Deploy-site adoption guidance says the deploy hook is invoked only with a mode input, but the hub task actually passes additional with: inputs (bundle-path, release-id, and environment). Because composite actions fail when given undeclared inputs, the docs should spell out the full set of inputs adopters must declare in .github/actions/deploy/action.yml.
**Deploy-site.** A site repo keeps `deploy-site.yml` as a per-repo caller (it has no manifest-wide catalog snippet either, since its `uses:` now names the hub, and it still carries the dispatch, the ref gate, and the shared validation call), but its `deploy` job reaches the hub-hosted `deploy-site-task.yml` and binds the same `environment:` the task binds, which is what lets the one crossing secret, `DEPLOY_SSH_PRIVATE_KEY`, resolve from the GitHub Environment store at the call site rather than through `secrets: inherit`, unusable across repositories. The three scripts `deploy/make-release.sh`, `deploy/prune-releases.sh`, and `checks/check-live-urls.sh` fold into one required `deploy` hook, `.github/actions/deploy/action.yml`, invoked three times with a `mode` input (`build`, `prune`, `verify`) so the site keeps its own generator, precompression, and URL contract while the upload-then-flip sequence stays hub-owned. Blog's own copy already carries more than three clean scripts, an `install-hugo` composite action, a git-mtime restore step, and PANGOLIN tokens for its staging auth check, which is why a hook, not a path convention, is the better contract here: it gives a site exactly this freedom instead of constraining it to fixed script names.

Copilot review on cd78cae: both the hub task's header comment and the
adoption doc said the deploy hook runs three times with a mode input,
but the three invocations also pass bundle-path, release-id, and
environment between them, and a composite action rejects an
invocation supplying an input it does not declare. States the full
four-input contract (mode, bundle-path, release-id, environment) in
both places, so an adopter's .github/actions/deploy/action.yml
declares what every invocation actually needs.
Copilot AI review requested due to automatic review settings August 16, 2026 18:49
@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering the suppressed (low-confidence) findings from the review on cd78cae.

The header comment says the deploy hook is invoked only with a mode input, but the workflow actually passes additional with: inputs (bundle-path, release-id, and later environment). Since composite actions reject undeclared inputs, the comment should reflect the full hook input contract so adopters implement a compatible .github/actions/deploy/action.yml.

The Deploy-site adoption guidance says the deploy hook is invoked only with a mode input, but the hub task actually passes additional with: inputs (bundle-path, release-id, and environment). Because composite actions fail when given undeclared inputs, the docs should spell out the full set of inputs adopters must declare in .github/actions/deploy/action.yml.

Both correct, fixed in ac94267. Both the hub task's header comment and the adoption doc now state the full four-input contract (mode, bundle-path, release-id, environment), since a composite action rejects an invocation supplying an input it does not declare, even one only some of its three modes use.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (3)

.github/workflows/deploy-site-task.yml:130

  • The deploy hook is invoked as a composite action, but no GitHub Environment vars are exported to it. Composite actions can’t read the vars context directly, so the hook can’t reliably use SITE_BASE_URL / DEPLOY_SSH_* without hardcoding unless the workflow passes them via env: (or inputs).

This issue also appears in the following locations of the same file:

  • line 207
  • line 217
    .github/workflows/deploy-site-task.yml:221
  • The verify hook is a composite action call but doesn’t receive SITE_BASE_URL (or other GitHub Environment vars) as env vars. Because composite actions can’t read the vars context directly, the hook won’t be able to verify against the live site without hardcoding unless these values are passed in.
    .github/workflows/deploy-site-task.yml:211
  • The prune hook is a composite action call, but it isn’t given the environment-derived connection details as env vars. Since composite actions can’t access the vars context directly, prune can’t use DEPLOY_SSH_HOST / DEPLOY_SSH_USER (or SITE_BASE_URL if needed) unless they’re passed from the workflow.

Copilot review on ac94267: the build, prune, and verify hook
invocations relied on the hook reading SITE_BASE_URL and
DEPLOY_SSH_USER/HOST through the vars context directly, but a
composite action's own steps are not guaranteed to see the caller's
vars context, the same class of restriction already established for
secrets. Passes each mode the values it needs as plain env vars
instead, the same mechanism the hub-owned upload and flip steps
already use, and documents the handoff in both the header comment and
the adoption doc.
Copilot AI review requested due to automatic review settings August 16, 2026 19:00
@ptr727

ptr727 commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

Answering the suppressed (low-confidence) findings from the review on ac94267, all the same underlying issue across the three hook invocations.

The deploy hook is invoked as a composite action, but no GitHub Environment vars are exported to it. Composite actions can't read the vars context directly, so the hook can't reliably use SITE_BASE_URL / DEPLOY_SSH_* without hardcoding unless the workflow passes them via env: (or inputs).

The verify hook is a composite action call but doesn't receive SITE_BASE_URL (or other GitHub Environment vars) as env vars ... the hook won't be able to verify against the live site without hardcoding unless these values are passed in.

The prune hook is a composite action call, but it isn't given the environment-derived connection details as env vars ... prune can't use DEPLOY_SSH_HOST / DEPLOY_SSH_USER (or SITE_BASE_URL if needed) unless they're passed from the workflow.

Correct, fixed in 0081e59. I could not find a documented statement either confirming or ruling out vars context access inside a composite action's own steps (only secrets is explicitly documented as unavailable there), but the fix costs nothing and removes the ambiguity either way: each invocation now passes the GitHub Environment variables that mode needs (SITE_BASE_URL for build and verify, DEPLOY_SSH_USER/DEPLOY_SSH_HOST for prune) as plain env: vars, the same mechanism the hub-owned upload and flip steps already use. Documented in both the header comment and the adoption doc.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit bdad5a7 into develop Aug 16, 2026
8 checks passed
@ptr727
ptr727 deleted the feature/type-specific-tasks branch August 16, 2026 19:06
ptr727 added a commit that referenced this pull request Aug 16, 2026
… Default (#768)

Promote `develop` to `main`, carrying the remaining stages of the
hub-hosted reusable-workflow rollout and the changes that landed beside
them:

- #759 Host Get-Version and Publish-Plan as Hub Reusable Tasks
- #760 Host the Validate Task and Reshape the Test Pull Request Stub
(settles #729 by design: the hub's validate task runs `uvx
<tool>@latest`, since Dependabot tracks the action pins and not a uvx
version)
- #761 Host the Type-Specific Tasks and Retire the Date Badge
- #762 Host the Release Chain and the Docker Core in the Hub
- #748 and #752, the staged rollout tracker and the PhotoCleaner
merge-bot pilot record
- #758 Flip the Fleet Line-Ending Default from CRLF to LF
- #753, #755, #756, #764, host-setup and test-collection changes

The release that follows this promotion is the first tag carrying every
hub task, so it is the pin the stage 2 to 5 adoptions and their catalog
snippets use. It is also the first run of the hub's own
`publish-release.yml` through `build-release-task.yml` with every target
disabled, which is the live proof that `github-release` runs when its
build needs are skipped.

Closes #729.
Refs #521 (hub half shipped, the merge-bot adoption sweep is what
remains).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants