From e75fd39601d5466836dc3feaeba97925c883bb38 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 25 Jul 2026 13:28:44 -0700 Subject: [PATCH 1/3] Fix hub bugs surfaced by the config-repo convergence (#447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five fixes routed back from the ESPHome-Config convergence (ESPHome-Config#65), items 7/3/1/4/8: - **configure.sh** `check_ruleset` guards a missing payload (`fail; return`) instead of aborting under `set -Eeuo pipefail` - it was dying on exactly the drift `check` exists to catch. Matches `check_settings`. - **AGENTS.md Release Model** dead cross-reference fixed (named README, linked WORKFLOW.md, heading existed in neither) - verbatim, re-vendors. - **.markdownlint-cli2.jsonc** MD033 gains `allowed_elements: [details, summary]` (GitHub collapsibles have no markdown equivalent; all other HTML still flags). - **Python tasks snippet** notes the Scripts-profile `uvx` adaptation. - **ESPHome-Config driftNote** reconciled (clang-format now added; subtree gates on ruff + pyright). Deferred as a scope question (item 2): ESPHome's stale `configure.sh`/`.markdownlint-cli2.jsonc` were routine past-revisions, not the hand-forks the divergences.json dispositions cover, so it does not belong in those curated lists. Items 5/6/clang-format await the maintainer. `spec/validate.py` OK; `spec/audit.py --selftest` PASS. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 --- .markdownlint-cli2.jsonc | 3 ++- AGENTS.md | 2 +- catalog/snippets/configs/vscode-tasks-python.json | 2 ++ registry/repos.json | 2 +- repo-config/configure.sh | 1 + 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc index e570ccef..01b4059c 100644 --- a/.markdownlint-cli2.jsonc +++ b/.markdownlint-cli2.jsonc @@ -3,7 +3,8 @@ // Prose paragraphs and data-heavy tables/URLs are intentionally long. // Reflowing at 80 cols hurts readability and churns diffs. "MD013": false, - // MD033 (inline HTML) stays enabled: HTML comments (reference-link dividers) pass it, and elements are flagged so native markdown wins. + // MD033 (inline HTML) stays enabled so native markdown wins - HTML comments (reference-link dividers) pass it, and details/summary are allowed for GitHub collapsibles, which have no markdown equivalent. Every other element still flags. + "MD033": { "allowed_elements": ["details", "summary"] }, // Require fenced code blocks over the legacy 4-space-indented style. "MD046": { "style": "fenced" }, // MD060 (table column style) is not enforced - allow both compact diff --git a/AGENTS.md b/AGENTS.md index 4688aa18..89735bf4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,7 +60,7 @@ A state-changing GitHub call is the highest-blast-radius thing an agent does her ## Release Model -The **two-phase model is the default**: PRs build fast, publishing is batched. See [README "Release Distribution Model"](./WORKFLOW.md) for the full rationale. The load-bearing rules follow. The auto-publish paths (bot push, schedule) apply to `release` repos. **Operational** repos differ - dispatch-only release, no auto-publish - see "Operational Repositories". +The **two-phase model is the default**: PRs build fast, publishing is batched. See [`WORKFLOW.md`](./WORKFLOW.md) for the full rationale. The load-bearing rules follow. The auto-publish paths (bot push, schedule) apply to `release` repos. **Operational** repos differ - dispatch-only release, no auto-publish - see "Operational Repositories". - **PRs smoke-test only.** [`test-pull-request.yml`](./.github/workflows/test-pull-request.yml) always runs unit tests, then a `dorny/paths-filter` `changes` job gates a **reduced** build of only the changed targets (Docker `linux/amd64` only, executable on a representative runtime subset), never pushing. Build-workflow files are intentionally not in the path filters - a filter can't tell a logic change from an action-version bump - so a workflow-only change isn't smoke-built; the reusable workflows are exercised by the next run that uses them (a later code PR's smoke build, or the scheduled/publish run). Workflow YAML is still linted in CI by the lint job's `actionlint` step; also run `actionlint` locally before pushing to catch issues early. - **A human merge never auto-publishes.** [`publish-release.yml`](./.github/workflows/publish-release.yml) is the sole publisher; each run builds the **single trigger branch** (`main` a release, `develop` a prerelease). A first `plan` job decides once whether the run publishes and every other job gates on its output. It publishes on a **`workflow_dispatch`** of `main`/`develop` (the human-initiated release), a **code-affecting bot push to `main`** (the codegen App merges every Dependabot/codegen PR, so `github.actor` is the gate - a human merge/promotion to `main` skips), or a **weekly `schedule`** (Docker only, to refresh the base image). The `push` is main-only and paths-filtered, so a develop bot merge and an Actions-only bump publish nothing. A source-only repo publishes on dispatch only. diff --git a/catalog/snippets/configs/vscode-tasks-python.json b/catalog/snippets/configs/vscode-tasks-python.json index 593f1078..49d99056 100644 --- a/catalog/snippets/configs/vscode-tasks-python.json +++ b/catalog/snippets/configs/vscode-tasks-python.json @@ -11,6 +11,8 @@ // `ruff format` -> `ruff check` -> the type checker. A repo whose CI type checker is mypy (pyright // editor-only) swaps the "Python Types" command to `uv run mypy src`; a pyright-strict repo keeps it // as `uv run pyright`. Adapt the target paths to the repo's package layout. + // A lint-only Scripts-profile subtree (no uv project, no `uv.lock`) runs the tools via `uvx ` + // instead of `uv run ` and omits the pytest, coverage, and `uv sync` tasks (CODESTYLE.md Python 'Two profiles'). { "label": "Ruff Format", "type": "process", diff --git a/registry/repos.json b/registry/repos.json index 023f6d51..b716092e 100644 --- a/registry/repos.json +++ b/registry/repos.json @@ -211,7 +211,7 @@ "requiredSecrets": [], "consumerModel": "pull", "releaseTrigger": "dispatch-only", - "driftNotes": ["ESPHome device config YAML consumed by the cataloged ESPHome-NonRoot image at runtime. Distinct from that Docker repo.", "First repo with a lint-only python codegen subtree (easystart/python: ruff/mypy config only, no tests, no uv.lock) and lint-only cpp (committed custom-component and template headers). A shared clang-format config for the cpp style is not yet added."] + "driftNotes": ["ESPHome device config YAML consumed by the cataloged ESPHome-NonRoot image at runtime. Distinct from that Docker repo.", "First repo with a lint-only python codegen subtree (easystart/python: ruff and pyright config only, no tests, no uv.lock) and lint-only cpp (committed custom-component and template headers, formatted by a shared clang-format config)."] }, { "name": "HomeAssistant-Config", diff --git a/repo-config/configure.sh b/repo-config/configure.sh index e42a5141..2f60bfad 100755 --- a/repo-config/configure.sh +++ b/repo-config/configure.sh @@ -175,6 +175,7 @@ gh_ok() { gh api "$@" >/dev/null 2>&1; } check_ruleset() { # payload-file - the live ruleset must match the committed policy, driven by the payload local file="$1" rname id live t want got wantc gotc want_enf + if [ ! -e "$file" ]; then fail "ruleset payload $file missing"; return; fi rname="$(jq -r '.name // empty' "$file")" if [ -z "$rname" ]; then fail "ruleset payload $file has no name"; return; fi if ! id="$(ruleset_id "$rname")"; then fail "ruleset '$rname' - could not resolve id"; return; fi From f6894e7c2ca54ea8c9040c01feedbc7c0a3cce5a Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 25 Jul 2026 13:35:00 -0700 Subject: [PATCH 2/3] Note the uvx substitution composes with the type-checker swap Copilot flagged that the Scripts-profile note said tools run via uvx but left the mypy/pyright type-checker swap reading uv run, implying a mismatched uvx/uv run copy for a mypy Scripts repo. State that the swap composes: uvx mypy src for a mypy repo, uvx pyright otherwise. Co-Authored-By: Claude Opus 4.8 --- catalog/snippets/configs/vscode-tasks-python.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/catalog/snippets/configs/vscode-tasks-python.json b/catalog/snippets/configs/vscode-tasks-python.json index 49d99056..6cf04846 100644 --- a/catalog/snippets/configs/vscode-tasks-python.json +++ b/catalog/snippets/configs/vscode-tasks-python.json @@ -11,8 +11,9 @@ // `ruff format` -> `ruff check` -> the type checker. A repo whose CI type checker is mypy (pyright // editor-only) swaps the "Python Types" command to `uv run mypy src`; a pyright-strict repo keeps it // as `uv run pyright`. Adapt the target paths to the repo's package layout. - // A lint-only Scripts-profile subtree (no uv project, no `uv.lock`) runs the tools via `uvx ` - // instead of `uv run ` and omits the pytest, coverage, and `uv sync` tasks (CODESTYLE.md Python 'Two profiles'). + // A lint-only Scripts-profile subtree (no uv project, no `uv.lock`) runs every tool via `uvx ` + // instead of `uv run ` - the type-checker swap above composes with it (`uvx mypy src` for a mypy repo, + // `uvx pyright` otherwise) - and omits the pytest, coverage, and `uv sync` tasks (CODESTYLE.md Python 'Two profiles'). { "label": "Ruff Format", "type": "process", From 3ea9a49589759d390136c7eca37b9861c4a641e8 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 25 Jul 2026 13:38:47 -0700 Subject: [PATCH 3/3] Reflow the Scripts-profile note to one sentence per line The uvx/type-checker note wrapped a single sentence across lines; the comment-layout convention requires one sentence per line with no wrapped sentences. Split it into three discrete sentences. Co-Authored-By: Claude Opus 4.8 --- catalog/snippets/configs/vscode-tasks-python.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/catalog/snippets/configs/vscode-tasks-python.json b/catalog/snippets/configs/vscode-tasks-python.json index 6cf04846..6d6a554c 100644 --- a/catalog/snippets/configs/vscode-tasks-python.json +++ b/catalog/snippets/configs/vscode-tasks-python.json @@ -11,9 +11,9 @@ // `ruff format` -> `ruff check` -> the type checker. A repo whose CI type checker is mypy (pyright // editor-only) swaps the "Python Types" command to `uv run mypy src`; a pyright-strict repo keeps it // as `uv run pyright`. Adapt the target paths to the repo's package layout. - // A lint-only Scripts-profile subtree (no uv project, no `uv.lock`) runs every tool via `uvx ` - // instead of `uv run ` - the type-checker swap above composes with it (`uvx mypy src` for a mypy repo, - // `uvx pyright` otherwise) - and omits the pytest, coverage, and `uv sync` tasks (CODESTYLE.md Python 'Two profiles'). + // A lint-only Scripts-profile subtree (no uv project, no `uv.lock`) runs every tool via `uvx ` not `uv run `. + // The type-checker swap above composes with it: `uvx mypy src` for a mypy repo, `uvx pyright` otherwise. + // It omits the pytest, coverage, and `uv sync` tasks (CODESTYLE.md Python 'Two profiles'). { "label": "Ruff Format", "type": "process",