Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,13 @@ One pull request stating that an agent never assumes a Docker image is present l

### Hub-Hosted Reusable Workflows

One pull request per phase moving a standard workflow out of every repo and into the hub as a `workflow_call` task, with a downstream caller stub and a composite-action hook for what is genuinely repo-specific. The design, the hook contract, the pin policy and the phases are in [`docs/reusable-workflows.md`][reusable-workflows-doc], and the burn-down is [`reports/workflow-reuse.md`][workflow-reuse-report], regenerated by `python3 spec/workflow_reuse.py --report`. The merge-bot phase shipped with the design, so this cluster starts at the gates.
One pull request per stage moving a standard workflow out of every repo and into the hub as a `workflow_call` task, with a downstream caller stub and a composite-action hook for what is genuinely repo-specific. The design, the hook contract, the pin policy and the staged rollout are in [`docs/reusable-workflows.md`][reusable-workflows-doc], and the burn-down is [`reports/workflow-reuse.md`][workflow-reuse-report], regenerated by `python3 spec/workflow_reuse.py --report`. The merge-bot task shipped with the design and its adoption is a sweep, so this cluster starts at the gates. The stage-by-stage completion state, one checkbox per hub change and per adopting repo with the evidence that closed it, is that doc's "Rollout" section, and a session resumes from there rather than from here.

**State** `ready` for the gates, `blocked` on the gates for everything after. **Touches** the hub's `.github/workflows/`, [`spec/files.json`][files], [`catalog/snippets/workflows/`][workflows], and [`WORKFLOW.md`][workflow] where a guarantee names a copied job. **Cost** one hub edit per phase plus an adoption per repo on its next visit, and no re-vendor beyond the stub each phase introduces.
**State** `ready` for the gates, `blocked` on the gates for everything after. **Touches** the hub's `.github/workflows/`, [`spec/files.json`][files], [`catalog/snippets/workflows/`][workflows], and [`WORKFLOW.md`][workflow] where a guarantee names a copied job. **Cost** one hub edit per stage plus an adoption per repo on its next visit, and no re-vendor beyond the stub each stage introduces.

- **Host the gates: `validate-task.yml` with a `validate` hook, and `test-pull-request-task.yml` with the fixed aggregator.** The hub owns the per-type doc-lint block once, the hook carries a repo's own tests, and the stub carries the trigger shape, operational or release. This phase is where the hook fallback is first proven live, on the hub for the default and on a pilot for the override.
- **Host the gates: `validate-task.yml` with a `validate` hook, and `test-pull-request-task.yml` with the fixed aggregator.** The hub owns the per-type doc-lint block once, the hook carries a repo's own tests, and the stub carries the trigger shape, operational or release. This stage is where the hook fallback is first proven live, on the hub for the default and on a pilot for the override.
- **Blocked by** - Nothing.
- **Issue** - None filed. [#585][issue-585] and [#729][issue-729] are settled inside this phase, the first by the operational stub's trigger and the second by the one place the hub validate task pins or floats its `uvx` tools.
- **Issue** - None filed. [#585][issue-585] and [#729][issue-729] are settled inside this stage, the first by the operational stub's trigger and the second by the one place the hub validate task pins or floats its `uvx` tools.
- **Checked** - `develop` at `7c67328` on 2026-08-15, where the report counts 20 copies of `test-pull-request.yml` in 13 variants and 13 copies of `validate-task.yml` in 11, and the doc-lint block (markdownlint, cspell, actionlint, editorconfig-checker) repeats in every one.
- **Open** - Whether the per-type lint steps are selected by an input the stub sets or read from the repo's registry entry through a hub checkout at `github.job_workflow_sha`, since the second needs no per-repo input and the first needs no network read.
- **Open** - Whether a `validate` hook that runs a domain compile (an ESPHome build, a KiCad ERC) is one hook or several, given the two repos carrying such a step run it as a separate job today.
Expand All @@ -282,13 +282,7 @@ One pull request per phase moving a standard workflow out of every repo and into
- **Issue** - None filed.
- **Checked** - `develop` at `7c67328` on 2026-08-15, where each of these has one or two carriers.
- **Open** - Whether ESPHome-NonRoot's second tracker, whose bump waits for a human, is the same task with `auto-merge: false` or stays repo-local.
- **Settled** - The `operational-vs-release-workflow` skill's note that a target-agnostic target list is "intentionally not done" is retired by this phase rather than before it, since it is true until then.

- **Land the merge-bot caller snippet in the catalog once a release names the task.** The snippet under `catalog/snippets/workflows/` is scanned by the pin gate and cannot carry a placeholder SHA, so it lands one release after the task.
- **Blocked by** - The first hub release carrying `merge-bot-task.yml`.
- **Issue** - None filed.
- **Checked** - `develop` at `7c67328` on 2026-08-15, where `scripts/repo_gate.py check_sha_pin` scans every `workflows/*.yml` path including the catalog.
- **Open** - Nothing, and the stub's text is already in [`docs/reusable-workflows.md`][reusable-workflows-doc] "Adopting the Merge-Bot".
- **Settled** - The `operational-vs-release-workflow` skill's note that a target-agnostic target list is "intentionally not done" is retired by this stage rather than before it, since it is true until then.

- **Decide the three merge-bot inputs the design leaves open.** The `delete-branch` default, the Dependabot semver-major filter two repos carry, and a `requiredHubUses` audit contract.
- **Blocked by** - Nothing, and each is a maintainer call rather than a finding.
Expand Down
1 change: 1 addition & 0 deletions catalog/snippets/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ The reusable build/publish workflow tasks a code-shipping repo runs. They are **

| File | Role | WORKFLOW.md guarantees |
| --- | --- | --- |
| `merge-bot-pull-request.yml` | Caller stub for the hub-hosted merge-bot task, pinned to a hub release, the shape every repo carries | D8.1, D8.3, D8.4 |
| `build-release-task.yml` | Multi-target release orchestrator: get-version, validate-release, github-release plus per-target build jobs | D3, D4, D5, D6 |
| `get-version-task.yml` | NBGV version/tag computation (reusable) | D3 |
| `publish-plan-task.yml` | Single-source release-gate decision (publish? stable?) reused by every publish-release job | D4 |
Expand Down
27 changes: 27 additions & 0 deletions catalog/snippets/workflows/merge-bot-pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Merge bot pull request action

# Thin caller: the merge-bot is the hub's reusable merge-bot-task.yml, which every fleet repo reaches rather than carries.
# The trigger is pull_request_target so the called workflow resolves from the trusted base rather than the PR head, and no job checks out PR code.
on:
pull_request_target:
types: [opened, reopened, synchronize]

# Concurrency keys on the PR number rather than on github.ref, which under pull_request_target is the base branch and would serialize every bot PR against it, so each PR queues independently.
# The cancel-in-progress setting is false so a follow-up synchronize does not cancel an in-flight opened run before it enables auto-merge.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: false

# Every write in the called workflow uses the App token, so GITHUB_TOKEN gets no scope.
permissions: {}

jobs:

merge-bot:
name: Merge bot pull request job
uses: ptr727/ProjectTemplate/.github/workflows/merge-bot-task.yml@20616e0a70613ad8727d567990f5d0e082f5275c # 2.0.338
secrets:
CODEGEN_APP_CLIENT_ID: ${{ secrets.CODEGEN_APP_CLIENT_ID }}
CODEGEN_APP_PRIVATE_KEY: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}
# A repo with a tracker outside the built-in codegen and upstream-version pairs adds a with: block carrying a rules JSON array of head or head-prefix plus base.
# A repo that keeps the repository-wide auto-delete off and still wants bot branches gone sets delete-branch true in the same block.
Comment thread
ptr727 marked this conversation as resolved.
Loading