Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Promote develop to main by ptr727 · Pull Request #252 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote develop to main - #252

Merged
ptr727 merged 8 commits into
mainfrom
develop
Jul 7, 2026
Merged

Promote develop to main#252
ptr727 merged 8 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Two-phase promotion of the accumulated governance work on develop to main. Carries #244-#251:

All commits were reviewed and CI-green on their individual develop PRs.

ptr727and others added 8 commits July 5, 2026 15:00
…hy (#244)
Document the governing motivation behind the branch, release, and versioning rules: pull-vs-push distribution grounds release restraint; dual-target branch sync enables the signed/linear/no-back-merge model; the 2-digit version carries human meaning while NBGV guarantees monotonic builds; contracts fix outcomes not implementations and favor reuse.
pyright strict on first-party code is the required baseline; mypy is allowed (required for HA platinum, useful for pydantic). Records default vs HA Python profiles; fixes the CODESTYLE-vs-AGENTS/catalog contradiction.
Both operational. Apply the registry corrections the audits found (aiopurpleair releaseTrigger -> publish-on-merge; drop stale HA cspell driftNote). Curated scientific symbols in the HA report preserved per maintainer instruction.
#247)
Resolves the classification backlog (22 cataloged, 0 backlog). Adds the eda type and releaseTrigger none per maintainer decisions; non-conformant branch states flagged for correction.
…ewhere) (#248)
One env.PLATFORMS computes the platform list; multi-arch only when publishing the main image, amd64 otherwise; QEMU gated on contains(env.PLATFORMS, 'arm64'). WORKFLOW.md Docker guarantees updated.
#249)
Hub publisher -> dispatch-only single-branch; WORKFLOW.md contract rewritten to the symmetric single-branch model (matrix and IGNORE_GITHUB_REF retired).
Workflow YAML is declared LF in .editorconfig (Dependabot/Actions write LF), hub workflows converted, editorconfig-checker (EOL-only) added to the lint gate. git leaves endings alone; nothing renormalized. AGENTS.md updated.
…251)
Three line-ending governance completions:
- **AGENTS.md** — encode that non-executed pattern files stay CRLF:
`.dockerignore`/`.gitignore` parsers strip a trailing CR, so only an
interpreted Dockerfile is LF. (Corrects a misclassification from the
fleet rollout where `.dockerignore` was wrongly LF-pinned.)
- **`.editorconfig`** — pin `catalog/snippets/workflows/*.{yml,yaml}` to
LF and convert the 11 snippets, so a copied snippet lands compliant with
the workflow-YAML-LF rule.
- **spec `recurring.eol`** — add the workflow-YAML LF pin to the
assertion (#250 left it listing only the older pins).
EOL-preserving; `git diff --ignore-cr-at-eol` shows only these three
content changes plus the snippet conversions. ec, jq, validate.py,
markdownlint, cspell all clean locally.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings July 7, 2026 02:04
@ptr727
ptr727 merged commit 8b9bd14 into mainJul 7, 2026
7 checks passed

CopilotAI 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

Promotes the accumulated governance and fleet-contract updates from develop to main, spanning the workflow/release contract, typing policy, audit report additions, registry/spec expansion, and line-ending enforcement.

Changes:

  • Update the release/workflow contract to a symmetric single-branch publish model, and align repo workflows accordingly.
  • Expand spec + registry to cover new repo classifications (eda, releaseTrigger: none) and incorporate new audit reports.
  • Tighten governance around typing (pyright strict baseline; mypy permitted/required for HA) and add CI enforcement for .editorconfig-defined line endings (workflow YAML pinned to LF).

Reviewed changes

Copilot reviewed 14 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
WORKFLOW.mdUpdates the CI/CD contract to the single-branch publish model and related guarantees.
spec/project-types.jsonExtends project-type checks (HA + EDA) and updates workflow anchors/typing expectations.
reports/homeassistant-purpleair/audit.mdAdds an audit report for a Home Assistant integration repo.
reports/aiopurpleair/audit.mdAdds an audit report for a Python/PyPI repo.
registry/repos.schema.jsonExtends schema to allow releaseTrigger: none.
registry/repos.jsonCatalogs backlog repos; updates release triggers and drift notes.
cspell.jsonAdds new allowed spelling (pydantic).
CODESTYLE.mdReplaces mypy ban with a strong-typing policy (pyright strict baseline; mypy allowed/required for HA).
catalog/snippets/workflows/run-periodic-codegen-pull-request.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/run-codegen-pull-request-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/publish-docker-readme-task.ymlNormalizes snippet formatting/EOL to match workflow YAML policy.
catalog/snippets/workflows/get-version-task.ymlNormalizes snippet formatting/EOL; retains NBGV invocation snippet.
catalog/snippets/workflows/check-upstream-version-task.ymlNormalizes snippet formatting/EOL; retains upstream-version tracker skeleton.
catalog/snippets/workflows/build-release-task.ymlNormalizes snippet formatting/EOL; retains orchestrator release-task shape.
catalog/snippets/workflows/build-pypilibrary-task.ymlNormalizes snippet formatting/EOL; retains PyPI build-only reusable workflow.
catalog/snippets/workflows/build-nugetlibrary-task.ymlNormalizes snippet formatting/EOL; retains NuGet build/publish reusable workflow.
catalog/snippets/workflows/build-executable-task.ymlNormalizes snippet formatting/EOL; retains executable build reusable workflow.
catalog/snippets/workflows/build-docker-task.ymlImplements branch-conditional Docker platforms (multi-arch main publish, amd64 otherwise).
catalog/snippets/workflows/build-datebadge-task.ymlNormalizes snippet formatting/EOL.
AGENTS.mdAdds Foundational Principles and extends line-ending governance for workflow YAML/pattern files.
.github/workflows/test-pull-request.ymlAdds editorconfig-checker CI step for line-ending validation.
.github/workflows/publish-release.ymlUpdates publisher to dispatch-only single-branch publish for this source-only repo.
.github/workflows/merge-bot-pull-request.ymlNormalizes formatting/EOL; retains merge-bot behavior.
.editorconfig-checker.jsonConfigures editorconfig-checker to enforce EOL-only (disables other checks).
.editorconfigPins workflow YAML (and snippet workflow YAML) to LF.

Comment on lines +66 to +73
- name: Publish to NuGet.org step
if: ${{ inputs.push }}
run: |
set -euo pipefail
dotnet nuget push ${{ runner.temp }}/publish/*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key ${{ secrets.NUGET_API_KEY }} \
--skip-duplicate
Comment threadAGENTS.md

The specific rules in this file implement a few governing principles. Read these first: they are the reason the branching, release, and versioning rules are shaped the way they are, and every rule below serves one of them.

- **Distribution respects the user: pull by default, push only where the channel forces it.** Docker images, GitHub Releases, and NuGet/PyPI packages are **pull** - the user decides when to consume them. A few channels are **push**: HACS surfaces a new release to every installed user as a pending update they did not go looking for, and a consumer that vendors from `main` picks up its current state. Because a release can reach users who did not ask for it, releasing is a deliberate act that marks a real functional change - never mechanical churn. This is the root of the two-phase default - merges do not publish, with `PUBLISH_ON_MERGE` as an explicit opt-in override - together with the no-op republish guarantee and maintainer-gated version bumps: a needless release spends the user's attention and, on a push channel, acts on their machine.
| branch-model | drift | pass | drift | both branches protected, live rulesets `enforcement: active`; develop allows only `squash` + `required_linear_history`, main allows only `merge` (`ruleset-{develop,main}.json`), required check `context: "Check pull request workflow status job"` matches the aggregator `name:` (`test-pull-request.yml:34`). Normalized diff: committed rulesets omit `required_reviewers: []` (live carries it) and top-level `bypass_actors` (live: `RepositoryRole 5 always` on both). Off-baseline filenames; extra live "Tag Rules" ruleset unrepresented - see Drift |
| repo-setup | pass | pass | pass | actions + dependabot stores both hold `CODEGEN_APP_CLIENT_ID`/`CODEGEN_APP_PRIVATE_KEY` (baseline merge-bot App, `merge-bot-pull-request.yml:45-46`) and `CODECOV_TOKEN` (codecov mechanism, used at `test-release-task.yml:204-207`); no forbidden `NUGET_API_KEY`/`PYPI_API_TOKEN`/`CODEGEN_APP_ID`; no publish secrets (github-release needs none). `CODECOV_TOKEN` in the dependabot store is beyond the mechanism's `stores: ["actions"]` but harmless |
| linter-parity | pass | pass | pass | one config each drives editor+CLI+CI, and CI runs each: markdownlint via `.markdownlint-cli2.jsonc` (`test-release-task.yml:108-110`), cspell via `cspell.json` `language: en-US` (`:113-119`), ruff via `.ruff.toml` (`:49-53`), pyright via `pyrightconfig.json` (`:91-95`), actionlint (`:121-122`), shellcheck (`:125-126`), plus mypy `--strict` (`:71-73`) |
| recurring-violations | drift | pass | drift | comments concise; spelling `en-US` (`cspell.json:3`); **EOL clean** - global `[*] end_of_line = crlf` default present (`.editorconfig:13`), LF pins for `*.sh`/`scripts/*` (`:29-33`) mirrored in `.gitattributes:12-13`, byte scan of all tracked files shows 68 CRLF / 6 correctly-LF (`scripts/*`, `repo-config/configure.sh`) / **0 mixed**. **Letter miss (charset):** em-dash (U+2014) and arrow (U+2192) pervade Python source comments/docstrings (`config_flow.py:120,136,...`, `coordinator.py:140`, `sensor.py`, `tests/*`) where ASCII equivalents (` - `, `->`) apply; docs are ASCII-clean; scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are within the carve-out. See Drift |

1. **`publish-release.yml` blanket-deletes the whole run artifact set - the D5.5 anti-pattern.** `cleanup-artifacts` runs `gh api repos/.../actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[].id'` and DELETEs every id (`publish-release.yml:114-122`). WORKFLOW.md D5.5 states cleanup MUST NOT enumerate and delete the run's whole artifact set, precisely because it also destroys diagnostic/log artifacts and auto-emitted build-records. Harmless today (the pure-Python pipeline emits no build-records, and `purpleair-zip` already sets `retention-days: 1`, `build-release-task.yml:107`), but it is a letter violation of a MUST-NOT and would be destructive if a Docker/other target is added. Fix: delete `purpleair-zip` by exact name at its consumer (the release job) and rely on the retention backstop, dropping the blanket sweep.
2. **Python ruff/pyright config off-canonical.** ruff lives in `.ruff.toml` and pyright in `pyrightconfig.json`; `pyproject.toml` holds only `[tool.pytest.ini_options]` (`pyproject.toml:1`) and `[tool.pylint]` (`:32`), no `[tool.ruff]`/`[tool.pyright]`. `python.config.placement` letter miss. Intent satisfied (both linters present and CI-run). This is an HA convention (mirrors `home-assistant/core`); see Spec Updates for the profile that would bless it. (driftNote (a) confirmed.)
3. **Em-dash / arrow in Python source comments and docstrings.** U+2014 and U+2192 appear throughout `config_flow.py` (`:120,136,150,187,207,441,459,497,705`), `coordinator.py:140`, `diagnostics.py:43`, `sensor.py`, and `tests/components/purpleair/*` where ASCII equivalents (` - `, `->`) apply. Governance docs (README/HISTORY/AGENTS/CODESTYLE/WORKFLOW) are ASCII-clean, and scientific symbols (µ, ³, ², §, ≤, ≥, ·, −) are the allowed carve-out. A `recurring.charset` letter miss confined to code comments.
Sign up for freeto 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

@ptr727