Template convergence barrier: absorb pins, generic release + docker-readme, carry-whole-file - #207
Merged
Conversation
Re-syncing the downstream fleet from the hub needs a durable, committed playbook (per-machine memory is lost on a machine switch). Add an "Orchestrated Re-Sync: Hub and Downstream Personas" subsection defining the hub/orchestrator vs downstream/derived separation of duties, and make the full-replacement rule explicit in "Staying in Sync": carried artifacts are replaced whole, never partially hand-merged, and re-syncing is not an occasion to add or grow comments. Guards the two recurring downstream regressions (partial carries, comment accretion); the orchestrator validates each result by diffing against the template. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pull_request_target switch (#201) changed github.ref from the PR merge ref to the base branch, so the concurrency group serialized every bot PR against a base into one queue - delaying auto-merge/disable when multiple Dependabot/codegen PRs are open. Key on github.event.pull_request.number so each PR's events still process in arrival order while different PRs run concurrently. Surfaced by Copilot on the NxWitness re-sync (#448). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lead the pins downstream already run: dependabot/fetch-metadata v2.5.0->v3.1.0 and softprops/action-gh-release v2.6.2->v3.0.1. Adopt PlexCleaner's fail_on_unmatched_files guard on github-release so a promised release-asset that goes missing/misnamed fails loudly; document that a no-file-target (Docker-only) repo relaxes it, and frame the generic release model (tag + source zip + README + LICENSE, targets amend). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…task The Docker Hub overview and build badge have no per-branch context - only main should update them. Move both out of the publisher's branch matrix into single jobs gated on main being published (caller-gated), dropping the per-leg self-gate and the branch input. Rebuild publish-docker-readme as a generic task: matrix over a repositories input (single- or multi-image), caller-passed ref, and an optional transform step (e.g. m4) to render the readme before pushing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the 'drop the sections/blocks you don't ship' guidance with a carry-whole-file rule: an inert [*.cs] editorconfig block or an unused-language CODESTYLE.md section costs nothing, and keeping it makes every re-sync a clean wholesale overwrite instead of an error-prone partial merge. Only per-language .vscode/tasks.json definitions still track the repo's own language, since the template ships only the task groups it uses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Phase-0 template-to-downstream convergence changes, focusing on making the template the source of truth for action pins and a more generic release + Docker README + date badge publishing model so derived repos can re-sync with minimal bespoke edits.
Changes:
- Update action pins used by core workflows (Dependabot metadata fetch and GitHub release creation).
- Refactor
publish-releaseso the date badge and Docker Hub README publish once (caller-gated to main being published), rather than per-branch matrix legs. - Generalize the reusable Docker README publishing workflow to support multiple repositories and an optional README transform step.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| AGENTS.md | Document the "carry whole shared file" re-sync rule and add the hub/downstream persona playbook for fleet-wide re-syncs. |
| .github/workflows/publish-release.yml | Gate date badge and Docker README publishing on main being included in the publish plan (single job, not per-branch matrix). |
| .github/workflows/publish-docker-readme-task.yml | Generalize Docker Hub README publishing (multi-repo matrix + optional transform step + caller-provided ref). |
| .github/workflows/merge-bot-pull-request.yml | Key concurrency by PR number under pull_request_target and bump dependabot/fetch-metadata pin. |
| .github/workflows/build-release-task.yml | Bump softprops/action-gh-release pin and enable fail_on_unmatched_files for release file patterns. |
| .github/workflows/build-datebadge-task.yml | Remove internal branch gating and rely on the caller to invoke only when main is being published. |
…otection A CRLF shebang breaks execution, and a broad normalization pass already swept extensionless s6 service scripts to CRLF (since .editorconfig can't match a file with no extension and '* -text' enforces nothing). Add '*.sh text eol=lf' as a carried pin and document that any repo shipping extensionless executables (s6 init, hooks) adds an explicit path pin like 'Docker/s6-overlay/** text eol=lf' so git holds them at LF on checkout and --renormalize. Also fold the stale '[*.cs] is .NET-only' note into the carry-whole-file rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CRLF in a Dockerfile breaks RUN heredocs and line continuations. Add LF pins for Dockerfile/*.Dockerfile in .gitattributes and .editorconfig, and renormalize the template's own Docker/Dockerfile (was CRLF) to LF. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The s6 service-script case is one instance of a general pattern: a file class the .editorconfig extension rules and '* -text' don't cover needs an explicit .gitattributes pin matching its tool's native ending. Call out husky/git hook scripts (.husky/pre-commit) alongside s6, and extend the rule to tool-owned formats like KiCad project/footprint/3D files (*.kicad_mod/*.kicad_sym/*.step, LF) so a sweep can't churn them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rule The two workflows rewritten in Phase 0 came out LF while every other workflow is CRLF per .editorconfig - normalize them back to CRLF (EOL-only). Document the decision rule the fan-out surfaced: CRLF is the default, LF only where a type requires it or CRLF breaks consumption (scripts/shebangs, Dockerfiles, KiCad); YAML stays CRLF (Actions tolerates it; yamllint defers via new-lines: disable). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… transform Eliminate the per-repo glue the fan-out surfaced: a multi-image repo passes manifest + manifest-jq (a get-repos job derives the matrix from e.g. Matrix.json) instead of hand-rolling its own derivation in the caller, and a render that needs a build artifact passes transform-artifact (downloaded before transform-run) instead of smuggling gh run download into the command. repositories stays supported for single-image repos. Also add the carried-file self-containment rule to AGENTS.md (no template/demo/cross-project references). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'never names a build job' rule is about the download step (pattern, not artifact-ids); the producing build jobs still appear in needs for sequencing - spell that out so it isn't misread as dropping needs. And exempt the sanctioned upstream-drift-report pointer from the no-cross-repo-references rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, precise EOL wording Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reword the .NET/Python section intros and the Quick Start so they say carry the file whole and ignore unused sections, not drop them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… pins The header read "Leave line endings alone" while the file pins LF for scripts/Dockerfiles; state the default-plus-exceptions policy accurately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce tasks) CODESTYLE no longer enumerates the convenience/project tasks; clarify that only the clean-compile task names are CODESTYLE-owned, the rest are the repo's adapt zone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7 tasks
…mage Per the issue #208 decisions: SHA-pinning covers repo-owned build-layer leaves too (Dependabot bumps SHA pins), and a multi-image repo uses a per-image registry buildcache tag rather than type=gha. Close the ambiguity so downstreams stop flagging these as drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the Copilot re-review of the convergence PRs:
- .gitattributes header: state that .editorconfig guides the editor while
the eol=lf pins are git's own enforcement (git does not read .editorconfig).
- CODESTYLE.md: make the .NET/Python section intros conditional ("any ...
project(s)") so they no longer contradict the carry-whole "ignore if absent"
preamble.
- AGENTS.md: generalize "merging is not releasing" so the publish step covers
an opted-in publish-on-merge trigger, not only scheduled/manual dispatch.
- build-datebadge-task.yml: pin RubbaBoy/BYOB to the v1.3.0 release SHA
(24f4642) per the action-pinning rule; the floating "# v1" violated it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot stopped reviewing on behalf of
ptr727 due to an error
June 25, 2026 14:25
# Conflicts: # AGENTS.md
ptr727
added a commit
that referenced
this pull request
Jul 4, 2026
Promote the accumulated `develop` work to `main`. Twelve changes since the last promotion: - Add `WORKFLOW.md`: workflow style, architecture, behavioral contract, test methodology (#223) - Use NBGV `IGNORE_GITHUB_REF` instead of the ineffective `GITHUB_REF` override (#222) - Skip validate-release on smoke builds (#220) - Gate asset delete on the release create/refresh condition (#218) - Ship branch rulesets as versioned JSON in the re-sync / drift loop (#212) - Clean up transfer artifacts surgically at consumption, not blanket-delete (#216) - Version each publish leg against its own branch; validate at entry (#215) - Consolidate workspace configurations into a unified ProjectTemplate workspace (#210) - Template convergence barrier: absorb pins, generic release + docker-readme, carry-whole-file (#207) - Key merge-bot concurrency on PR number, not `github.ref` (#206) - Codify orchestrated re-sync personas and full-replacement rule (#205) - Lead action pins; affirm pattern-based artifact handoff (#204) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase-0 of the template-to-downstream convergence sweep: the template absorbs everything downstream repos had reinvented, so derived repos can take the carried artifacts verbatim with zero hand-rolling. Builds on #205 (orchestration personas) and #206 (per-PR merge-bot concurrency), which this branch incorporates. Held for end-gate review; not to be merged midway.
What this PR does
dependabot/fetch-metadatav2.5.0 -> v3.1.0 andsoftprops/action-gh-releasev2.6.2 -> v3.0.1, the SHAs the downstreams already run.release-asset-<branch>-*artifacts (binaries/packages) or pushing elsewhere (image/registry).github-releasecollects assets by therelease-asset-<branch>-*pattern so the job is target-agnostic and carries verbatim.mainbeing published.publish-docker-readme-task.ymlis rebuilt generic: a matrix over arepositoriesinput (or a manifest-derived list), a caller-passedref, and an optional transform step (e.g. m4) to render the README before pushing.[*.cs]block or unused-languageCODESTYLE.mdsection costs nothing), so every re-sync is a clean overwrite, not a partial merge.CODESTYLE.mdis genericized into a self-contained drop-in (no demo-project names, no template-onboarding pointers) and carries both the .NET and Python sections whole. Only per-language.vscode/tasks.jsontask definitions still track the repo's language.All carried files stay self-contained (no template/demo/cross-project references except the sanctioned upstream-drift pointer). Workflow YAML and Markdown remain CRLF per the line-ending governance. Validated with actionlint and markdownlint-cli2 (clean).
Documented adaptations (for review)
These are the genuine, intentional deviations the maintainer should review at the end gate. Each is a sanctioned exception with its rationale recorded inline in the artifact; nothing here is accidental drift.
fail_on_unmatched_files: trueongithub-release- a promisedrelease-asset-*that goes missing or is misnamed fails the release loudly; a Docker-only / no-file-target repo is the one case that relaxes it (no release asset to attach).merge-bot-pull-request.ymlconcurrency: per-PR group,cancel-in-progress: false- underpull_request_targetgithub.refis the base branch, which would serialize every bot PR against that base, so the group keys on the PR number; cancellation would leave auto-merge in an inconsistent state, so events queue and each runs to completion.publish-release.ymlconcurrency: global ref-independent group,cancel-in-progress: false- it publishes shared ref-independent artifacts (both branches' Docker tags/caches and GitHub releases) regardless of the triggering ref, so a ref-scoped group would let a scheduled run and a manual dispatch double-push; cancelling mid-flight can leave a partial tag set or half-created release.dotnet/nbgvconsumed via@master(no SHA pin) - the upstream tag stream lagsmastersubstantially and Dependabot's tag-tracking would propose a downgrade; this is the one documented no-SHA-pin exception.Check pull request workflow statusintest-pull-request.ymlis referenced as a required-status-checkcontext:in the branch ruleset; renaming it to fit the "every job name ends in job" convention would silently break required-status-check enforcement.🤖 Generated with Claude Code