Flip the Fleet Line-Ending Default from CRLF to LF - #758
Merged
Conversation
GOVERNANCE.md#line-endings and the comment-and-doc-style Skill's references/line-endings.md now state LF as the [*] .editorconfig default, with CRLF pinned only for *.bat/*.cmd, the one type Windows itself requires it for. .gitattributes is unchanged: its existing execution-sensitive LF pins (*.sh, Dockerfiles, uv.lock, the shebang-executed .py by-path list) stay as git-level enforcement, now redundant with the new [*] default but kept per the maintainer's direction. The redundant per-type LF overrides that used to sit below the old CRLF default in .editorconfig are dropped, since the default now already gives every one of them LF. Includes a one-time renormalization of every tracked CRLF file in the hub to LF (160 files, verified content-identical via git diff --ignore-cr-at-eol), and updates every doc/spec/script that described or depended on the old CRLF-default assumption: GOVERNANCE.md, README.md, OPERATIONS.md, STANDUP.md, docs/host-setup.md, TODO.md (drops the now-superseded "Default .py to LF" cluster), spec/project-types.json, spec/validate.py, registry/repos.schema.json, the operational-vs-release-workflow and python-codestyle and skill-lifecycle and git-commit-conventions Skills, the check-upstream-version-task.yml and husky README catalog snippets, and the CRLF report-writers in spec/workflow_reuse.py and spec/fidelity_honesty.py. scripts/build_dist.py's plugin-manifest and digest-stamp writers now emit LF explicitly instead of CRLF, with scripts/tests/test_build_dist.py updated to match (the old CRLF assertion discriminated a missing explicit newline argument on Linux CI; that discrimination is not available in the LF direction, since LF is also Linux's platform default, so the test now asserts the output shape only, documented in its docstring). .claude-plugin/fleet-skills/ regenerated from the updated .agents/skills/ source via build_dist.py. Verified: editorconfig-checker, markdownlint-cli2, cspell, actionlint, repo_gate.py, prose_lint.py --diff, spec/validate.py, spec/audit.py --selftest, spec/workflow_reuse.py --selftest, and the scripts/tests/ + host-setup/agent-safety/test_install.py suites all pass.
docs/eol-lf-rollout.md checks off ProjectTemplate (converted in the prior commit) and Vantage-Config (not applicable, stays CRLF per its Windows-native consuming app), and lists the remaining 20 fleet repos from registry/repos.json unchecked, each with the conversion procedure that applies to its workflowModel/lineEndings shape. Hub-only, not carried downstream, the same way docs/fleet-map.md is hub-only. Verified: markdownlint-cli2, cspell, prose_lint.py --diff all pass.
# Conflicts: # scripts/tests/test_bootstrap.py
Contributor
There was a problem hiding this comment.
Pull request overview
Switches the fleet’s default line ending from CRLF to LF, keeping CRLF only for Windows batch/command scripts, and updates the hub’s tooling/docs/tests to treat LF as the canonical output going forward.
Changes:
- Update report writers and dist-generation writers to emit LF explicitly (and update tests accordingly).
- Renormalize tracked files to LF and update references in spec/docs/schemas to match the new default.
- Add a hub-only rollout checklist documenting how to apply the conversion across the rest of the fleet.
Reviewed changes
Copilot reviewed 34 out of 169 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| version.json | Renormalized to LF (no semantic change). |
| spec/workflow_reuse.py | Report writer now emits LF without CRLF translation. |
| spec/validate.py | Documentation comment updated to “fleet LF default”. |
| spec/third-party-tools.schema.json | Renormalized to LF (no semantic change). |
| spec/secrets.schema.json | Renormalized to LF (no semantic change). |
| spec/scope-model.md | Renormalized to LF (no semantic change). |
| spec/project-types.schema.json | Renormalized to LF (no semantic change). |
| spec/host-tools-local.schema.json | Renormalized to LF (no semantic change). |
| spec/files.schema.json | Renormalized to LF (no semantic change). |
| spec/fidelity_honesty.py | Report writer now emits LF without CRLF translation. |
| spec/divergences.schema.json | Renormalized to LF (no semantic change). |
| spec/divergences.json | Renormalized to LF (no semantic change). |
| spec/audit.py | Comment updated to reflect CRLF can still exist in edge cases, even though default is LF. |
| scripts/tests/test_build_dist.py | Update assertions to require LF-only output (no \r). |
| scripts/tests/test_bootstrap.py | Update comments/messages to match eol-coverage semantics under LF default. |
| scripts/skills_install.ps1 | Renormalized to LF (no semantic change). |
| scripts/build_dist.py | Force LF output explicitly via newline="\\n" for manifest + digest stamp. |
| reports/_template.md | Renormalized to LF (template for generated audit reports). |
| repo-config/settings.json | Renormalized to LF (no semantic change). |
| repo-config/operational/develop.json | Renormalized to LF (no semantic change). |
| repo-config/main.json | Renormalized to LF (no semantic change). |
| repo-config/develop.json | Renormalized to LF (no semantic change). |
| registry/repos.schema.json | Update schema comment to “fleet LF default”. |
| pyproject.toml | Renormalized to LF (no semantic change). |
| PSScriptAnalyzerSettings.psd1 | Renormalized to LF (no semantic change). |
| ProjectTemplate.code-workspace | Renormalized to LF (no semantic change). |
| LICENSE | Renormalized to LF (no semantic change). |
| host-tools.json | Renormalized to LF (no semantic change). |
| host-setup/agent-safety/install.ps1 | Renormalized to LF (no semantic change). |
| host-setup/agent-safety/claude-md-fleet.md | Renormalized to LF (no semantic change). |
| host-setup/agent-safety/.markdownlint-cli2.jsonc | Renormalized to LF (no semantic change). |
| HISTORY.md | Renormalized to LF (no semantic change). |
| docs/peer-messaging.md | Renormalized to LF (no semantic change). |
| docs/eol-lf-rollout.md | New hub-only rollout checklist for converting downstream repos. |
| catalog/snippets/workflows/README.md | Renormalized to LF (no semantic change). |
| catalog/snippets/workflows/check-upstream-version-task.yml | Stop forcing CRLF; write state file as LF to match new default. |
| catalog/snippets/vscode/README.md | Renormalized to LF (no semantic change). |
| catalog/snippets/vscode/python.jsonc | Renormalized to LF (no semantic change). |
| catalog/snippets/vscode/dotnet.jsonc | Renormalized to LF (no semantic change). |
| catalog/snippets/vscode/docker.jsonc | Renormalized to LF (no semantic change). |
| catalog/snippets/vscode/base.jsonc | Renormalized to LF (no semantic change). |
| catalog/snippets/husky/README.md | Update guidance to rely on global LF default + .gitattributes enforcement. |
| catalog/snippets/devcontainer/python/devcontainer.json | Renormalized to LF (no semantic change). |
| catalog/snippets/devcontainer/dotnet/devcontainer.json | Renormalized to LF (no semantic change). |
| catalog/snippets/configs/docker-hub-readme.md | Renormalized to LF (no semantic change). |
| catalog/snippets/configs/dependabot.yml | Renormalized to LF (no semantic change). |
| catalog/snippets/configs/codecov.yml | Renormalized to LF (no semantic change). |
| catalog/README.md | Renormalized to LF (no semantic change). |
| .markdownlint-cli2.jsonc | Renormalized to LF (no semantic change). |
| .gitignore | Renormalized to LF (no semantic change). |
| .github/dependabot.yml | Renormalized to LF (no semantic change). |
| .gitattributes | Renormalized to LF; contains a stale comment referencing “CRLF default” for .py. |
| .editorconfig-checker.json | Renormalized to LF (no semantic change). |
| .claude-plugin/marketplace.json | Renormalized to LF (no semantic change). |
| .claude-plugin/fleet-skills/skills/workflow-ci-contract/references/test-methodology.md | Renormalized to LF (generated skill content). |
| .claude-plugin/fleet-skills/skills/shell-codestyle/SKILL.md | Renormalized to LF (generated skill content). |
| .claude-plugin/fleet-skills/skills/fleet-conformance-check/SKILL.md | Renormalized to LF (generated skill content). |
| .claude-plugin/fleet-skills/skills/comment-and-doc-style/references/markdown-links.md | Renormalized to LF (generated skill content). |
| .claude-plugin/fleet-skills/skills/comment-and-doc-style/references/carried-doc-references.md | Renormalized to LF (generated skill content). |
| .claude-plugin/fleet-skills/skills/carried-instruction-file-guard/SKILL.md | Renormalized to LF (generated skill content). |
| .claude-plugin/fleet-skills/.source-digest | Update digest after regeneration. |
| .claude-plugin/fleet-skills/.claude-plugin/plugin.json | Renormalized to LF (generated plugin metadata). |
| .agents/skills/workflow-ci-contract/references/test-methodology.md | Renormalized to LF (canonical skill source). |
| .agents/skills/shell-codestyle/SKILL.md | Renormalized to LF (canonical skill source). |
| .agents/skills/README.md | Renormalized to LF (canonical skill source). |
| .agents/skills/fleet-conformance-check/SKILL.md | Renormalized to LF (canonical skill source). |
| .agents/skills/comment-and-doc-style/references/markdown-links.md | Renormalized to LF (canonical skill source). |
| .agents/skills/comment-and-doc-style/references/carried-doc-references.md | Renormalized to LF (canonical skill source). |
| .agents/skills/carried-instruction-file-guard/SKILL.md | Renormalized to LF (canonical skill source). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Copilot review on PR #758 caught this: the pins themselves are untouched (per the maintainer's direction that .gitattributes stays as-is), but two comments still described the old CRLF default as current. Both now describe the new LF default plus the operational-repo CRLF override that can still trigger the pin's rationale.
ptr727
added a commit
that referenced
this pull request
Aug 16, 2026
## 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](https://claude.com/claude-code)
ptr727
added a commit
that referenced
this pull request
Aug 16, 2026
## What Hosts the fleet's release chain and Docker core in the hub, per `docs/reusable-workflows.md` "Stage 4: The Release Chain and the Docker Core". - `.github/workflows/build-release-task.yml` (new hub task): inlines `get-version` and `validate-release`, per-target jobs for `build-executable`, `build-nuget`, `build-pypi` (each a hook resolved to the caller's own composite action or a hub default), a duplicated Docker core as `build-docker` (docker-prepare / docker-build-base hooks), and `github-release` verbatim in shape (pattern download, exists gate, `target_commitish`, consume-then-delete, `expect_release_assets`). - `.github/workflows/build-docker-task.yml` (new hub task): the same Docker core, published separately for a caller that wants only the Docker leg. The two files carry the same job body by hand, since a hub task cannot reach a sibling hub task by a `./` path. - Four hub-default composite actions: `build-executable-default`, `build-nuget-default`, `build-pypi-default`, `docker-prepare-default`, authored from today's leaf catalog snippets. - The hub's own `publish-release.yml` now calls `build-release-task.yml` with every target disabled and `expect_release_assets: false`, exercising the task's `get-version`, `validate-release`, and `github-release` jobs on the hub's own release. - Retires the five catalog snippets the new hub tasks replace (`build-release-task.yml`, `build-executable-task.yml`, `build-nugetlibrary-task.yml`, `build-pypilibrary-task.yml`, `build-docker-task.yml`), updates the catalog README, and adds a `retire` gap disposition to `spec/divergences.json` naming the ten carriers. - Gives `publish-release.yml` an `interface` contract in `spec/files.json` (`requiredJobKeys: ["plan", "validate", "publish"]`) with new `_selftest` cases in `spec/audit.py`, and updates `spec/fidelity-model.md`'s "The Workflow Override Seam Contract" paragraph for the stub-era shape. - Documents the caller-stub shape under a new "Adopting the Release Chain" section, and updates the Rollout tracker and `TODO.md` with the settled design decisions and proof items. - Rebased onto `#758`'s LF-default flip. Every file this PR touches is LF now. ## Design choices where the brief left room - **Docker core duplication.** `build-release-task.yml`'s `build-docker` job and `build-docker-task.yml` carry the same job body rather than one calling the other, since a hub task cannot nest a sibling hub task by a `./` path. `build-docker-task.yml` stays published for a caller that wants the Docker leg alone. - **No `publish-release-task.yml`.** A caller stub's `plan`/`validate`/`publish`/`publish-pypi` jobs are each a thin call to one hub task or a verbatim OIDC upload, and the trigger policy tying them together differs enough across the fleet's shapes (dispatch-only Docker schedule, push-gated NuGet/PyPI) that hosting it would only move the same `with:` block, not remove it. - **Project-path inputs on the executable/nuget/pypi hub defaults.** Beyond the fixed `ref`/`branch`/`smoke` set, the defaults take `project-file`/`project-dir`, mirroring the Docker hook's own `image` input, since no live repo's project folder matches the vanilla `Console`/`NuGetLibrary`/`PyPiLibrary` convention literally. - **`release-assets` hook deferred.** No cataloged repo needs an "extra files" hook today, so it is not shipped. The Hook Catalog table row for `build-release-task.yml` reflects only the hooks actually implemented. - **actionlint's `job_workflow_sha` gap.** `github.job_workflow_sha` (used to check out the hub at the caller's pinned commit) is a real GitHub Actions context property actionlint 1.7.12 does not yet recognize. Added a scoped `-ignore` flag to the hub's own `validate-task.yml` actionlint step rather than working around the design. ## What this cannot prove from the hub The hub has no build targets, so every behavioral claim (the hook fallback path, OIDC NuGet publishing, the Docker matrix/build-base hooks, a real release through the new task) is a proof item on the PhotoCleaner/PlexCleaner pilot adoption, tracked in `docs/reusable-workflows.md` Rollout Stage 4. Adoption itself is out of scope for this PR. ## Verification `spec/validate.py`, `python3 -m unittest discover -s scripts/tests`, `spec/audit.py --selftest`, `spec/workflow_reuse.py --selftest`, `scripts/repo_gate.py`, `scripts/build_dist.py --check`, `scripts/prose_lint.py` (full check set), `ruff check`/`format --check`, `mypy`, `actionlint` (with the one documented `-ignore`), `markdownlint-cli2`, and `editorconfig-checker` all pass.
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).
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.
What
Switches the fleet's line-ending default from CRLF to LF. Only
*.bat/*.cmdare pinned CRLFgoing forward, the one type Windows itself requires it for.
.gitattributesis unchanged perthe maintainer's direction, its execution-sensitive LF pins stay as git-level enforcement,
now redundant with the new
[*]default but retained.Why
Windows GUI tooling (VS Code, Visual Studio, Notepad, WordPad) all read and write LF cleanly,
and the fleet's own tooling (Dependabot,
uv, most editors) already writes LF by default, sothe CRLF default had become mostly a source of churn against tooling that keeps normalizing
back to LF, not a real Windows-compatibility requirement.
.ps1was verified to work fine onLF too.
Changes
.editorconfig:[*] end_of_lineflips tolf,*.bat/*.cmdpinnedcrlf, thenow-redundant per-type LF overrides (shell, Dockerfiles, workflow YAML,
uv.lock,shebang-executed
.pyby path, the husky hook) are dropped since the default alreadycovers them.
content-identical via
git diff --ignore-cr-at-eol).GOVERNANCE.md,README.md,OPERATIONS.md,STANDUP.md,docs/host-setup.md,TODO.md(drops the now-superseded "Default
.pyto LF" cluster, since a global LF default subsumesit),
spec/project-types.json,spec/validate.py,registry/repos.schema.json, severalSkills (
comment-and-doc-stylemost heavily, plusoperational-vs-release-workflow,python-codestyle,skill-lifecycle,git-commit-conventions), two catalog snippets, andthe CRLF report-writers in
spec/workflow_reuse.py/spec/fidelity_honesty.py.scripts/build_dist.py's plugin-manifest and digest-stamp writers now emit LF explicitlyinstead of CRLF;
scripts/tests/test_build_dist.pyupdated to match, with a note on why theold test's CRLF-on-Linux discrimination trick doesn't have an LF-direction equivalent.
.claude-plugin/fleet-skills/regenerated from the updated.agents/skills/source.docs/eol-lf-rollout.md, a hub-only checklist tracking the same conversion across theother 20 fleet repos (ProjectTemplate and Vantage-Config already checked off, the latter as
not-applicable since it stays CRLF for its Windows-native consuming app).
Verification
editorconfig-checker,markdownlint-cli2,cspell,actionlint,repo_gate.py,prose_lint.py --diff,spec/validate.py,spec/audit.py --selftest,spec/workflow_reuse.py --selftest, and thescripts/tests/+host-setup/agent-safety/test_install.pysuites all pass.🤖 Generated with Claude Code