diff --git a/README.md b/README.md index ffd9c874..b5859c3b 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Agent enablement for a fleet of repositories: autonomy and repeatable quality in ## Build and Distribution -- **Source Code**: [GitHub][projecttemplate-link] - source, issues, discussions, and CI/CD pipelines. -- **Versioned Releases**: [GitHub Releases][releases-link] - version-tagged source archives. +- **Source Code**: [GitHub][projecttemplate-link] for source, issues, discussions, and CI/CD pipelines. +- **Versioned Releases**: [GitHub Releases][releases-link] for version-tagged source archives. ### Build Status @@ -91,10 +91,10 @@ Keeping a fleet of repositories consistent has always been a tax paid in review ProjectTemplate follows the same model it documents, and audits its own rules against itself (it classifies as the source-only project type in [WORKFLOW.md][workflow]). -- **Branching.** Persistent `main` and `develop`, each with its own ruleset. This repo uses the default `release` workflow model: commit on feature branches only, feature branch to `develop` is squash-merged, `develop` to `main` is a merge commit, and `develop` is forward-only (no `main -> develop` back-merges). Live-service config repos instead use the `operational` model (registry `workflowModel`) - direct signed commits to `develop`, promoted to `main` by an occasional PR. See [GOVERNANCE.md "Branching Model"][governance-branching-model]. -- **CI is lint-only.** There is no build or unit test; the PR gate runs markdownlint, cspell, JSON validation (`jq` parses `registry/`, `spec/`, and `repo-config/`, plus the `spec/validate.py` cross-reference and shape checks), and actionlint, and exposes the ruleset-bound `Check pull request workflow status job` aggregator. The same lint configs (`.markdownlint-cli2.jsonc`, `cspell.json`) drive the editor extensions, the CLI, and CI. +- **Branching.** Persistent `main` and `develop`, each with its own ruleset. This repo uses the default `release` workflow model: commit on feature branches only, feature branch to `develop` is squash-merged, `develop` to `main` is a merge commit, and `develop` is forward-only (no `main -> develop` back-merges). Live-service config repos instead use the `operational` model (registry `workflowModel`), with direct signed commits to `develop`, promoted to `main` by an occasional PR. See [GOVERNANCE.md "Branching Model"][governance-branching-model]. +- **CI is lint-only.** There is no build or unit test. The PR gate runs markdownlint, cspell, JSON validation (`jq` parses `registry/`, `spec/`, and `repo-config/`, plus the `spec/validate.py` cross-reference and shape checks), and actionlint, and exposes the ruleset-bound `Check pull request workflow status job` aggregator. The same lint configs (`.markdownlint-cli2.jsonc`, `cspell.json`) drive the editor extensions, the CLI, and CI. - **Review loop.** Every PR is reviewed by GitHub Copilot, and the agent drives the review loop to green and merges only with explicit maintainer permission. See [GOVERNANCE.md "PR Review Etiquette"][governance-pr-review-etiquette]. -- **Release.** A `develop -> main` merge is promoted through a GitHub release (tag plus a source zip, README, and LICENSE); versioning is NBGV-driven from [version.json][version]. See [WORKFLOW.md][workflow]. +- **Release.** A `develop -> main` merge is promoted through a GitHub release (tag plus a source zip, README, and LICENSE). Versioning is NBGV-driven from [version.json][version]. See [WORKFLOW.md][workflow]. ## Rules @@ -103,23 +103,23 @@ A human-readable index of the rules agents enforce, implement, and audit. The au ### Always - Sign every commit (SSH or GPG). -- Branch feature -> develop (squash) -> main (merge commit); develop is forward-only. +- Branch feature -> develop (squash) -> main (merge commit), and develop is forward-only. - Drive every PR through the Copilot review loop and merge only with maintainer approval. - Write US English and ASCII only (no em-dash, straight quotes). -- Write docs and comments in the present tense, describing only the current state - never as a change from a prior one. +- Write docs and comments in the present tense, describing only the current state, never as a change from a prior one. - Keep comments concise and only for the non-obvious, and never grow them on edit. - Follow `.editorconfig` line endings (CRLF default, LF for shell and Docker) and preserve a file's endings on edit. - One logical paragraph per line, with a trailing `\` for an intentional hard break. - Pin every GitHub Action to a commit SHA with a version comment. - Share one lint config per tool across the editor, the CLI, and CI. - Run the repo's whole lint gate before pushing, not just the parts that look relevant. -- Make gates fail loud - a gate that stops gating must error or annotate, never pass silently. +- Make gates fail loud, since a gate that stops gating must error or annotate, never pass silently. - Favor VS Code tasks and launch configs for building, running, and testing over ad-hoc shell scripts. ### Never - Never force-push or rewrite shared history. -- Never treat a merge as a release; publishing is a separate, explicit step. +- Never treat a merge as a release. Publishing is a separate, explicit step. - Never blanket-delete a workflow run's artifacts. - Never store a static key when OIDC Trusted Publishing is available. @@ -129,11 +129,11 @@ A human-readable index of the rules agents enforce, implement, and audit. The au ### If a Python Project -- Configure ruff and a type checker in `pyproject.toml` - pyright strict, or mypy in CI with pyright editor-only; whichever runs in CI is the gate. +- Configure ruff and a type checker in `pyproject.toml`, either pyright strict or mypy in CI with pyright editor-only. Whichever runs in CI is the gate. ### If Both C# and Python -- Both sections above apply; a repo can be both (a C# app plus a Python subtree). The Python is either a full uv project (`uv.lock`, `uv run`) or a stdlib-only `uvx` scripts subtree (no `uv.lock`, `pyproject.toml` carries lint/type config only). See [CODESTYLE.md][codestyle] "Two profiles". +- Both sections above apply, and a repo can be both (a C# app plus a Python subtree). The Python is either a full uv project (`uv.lock`, `uv run`) or a stdlib-only `uvx` scripts subtree (no `uv.lock`, `pyproject.toml` carries lint/type config only). See [CODESTYLE.md][codestyle] "Two profiles". ### If Publishing a Package (NuGet or PyPI) diff --git a/TODO.md b/TODO.md index e07d430b..882da1ec 100644 --- a/TODO.md +++ b/TODO.md @@ -11,10 +11,13 @@ Running backlog for this repo, kept in a committed file so the guidance survives - Refresh the README (it has gone stale) and evaluate a lower-maintenance structure, for example a per-section index that points into each doc with a one-line description, keeping the README as the adoption and audit-instruction entry point with pointers to the other docs. A per-section index trades brevity for a sync obligation: it must track what the docs contain. - Add a linter-only Python project type for codegen/boilerplate Python, code that runs during another tool's build to emit generated source (e.g. ESPHome codegen that produces enriched C++ at compile time), so it ships no unit tests and no coverage and needs only the linter. Keep it distinct from the existing `python` type, which is utility code that can and should carry unit tests and coverage (as in PlexCleaner). Until it exists, ESPHome-Config stays `source-only` and its `+python` reclassification is deferred, so accept its one outstanding validation finding meanwhile. - Add a fleet-standard clang-format config for the `cpp` type: a catalog snippet plus a CODESTYLE C++ section defining the style, the C++ analogue of the shared ruff config, so the `cpp` clang-format check references one canonical style rather than each repo inventing its own. Base it on the ESPHome-Config agent's proposed `.clang-format`. -- Sweep the 12 `dash` and `semicolon` findings in `README.md`. The edits that made this conflict-prone have landed, so it is now free to do. +- Document in [`STANDUP.md`][standup] that the initial onboarding commits belong on a feature branch and reach `develop` by squash, never as direct commits to `develop`. Pre-creating `main` and `develop` is right, and committing onto `develop` from there is the wrong next step. Section 0 already teaches the same shape of trap for signing, that the window closes at the first commit, so branch placement belongs beside it. The two reasons do not carry equally across the workflow models, which is the part worth stating rather than assuming. On a `release` repo `repo-config/develop.json` carries a `pull_request` rule, so a direct commit is simply blocked and an agent discovers the rule by hitting it. On an `operational` repo `repo-config/operational/develop.json` carries only `deletion`, `non_fast_forward` and `required_signatures`, so a direct commit succeeds and nothing corrects it, which is exactly why the instruction has to carry the weight there. The reason that holds for both is that the squash collapses the exploratory onboarding history, which is where PII, secrets and noise commits accumulate, and a public repo treats that history as a hard gate. There is no recovery afterwards, since `non_fast_forward` is set on both `develop` payloads, so history that reaches `develop` cannot be rewritten without disabling the ruleset. Blog hit this during its standup and moved to a feature branch. +- Declare locally-required secrets the way GitHub-stored ones are already declared, and make a gitignored `secrets/` directory the fleet standard that holds them. [`spec/secrets.json`][secrets] covers only the Actions and Dependabot stores, so a repo that deploys somewhere has no declared way to say what it needs at runtime, and the required set is discoverable only by reading the deploy. The pattern already runs in the fleet in two shapes: HomeAutomation-Config keeps a gitignored secrets directory of env files and Docker secret files, and ESPHome-Config keeps a gitignored `secrets.yaml` beside a committed `_secrets.yaml`. The committed file carries the required names with dummy values, so the shape of the requirement is in git while the values never are, which is the same split the GitHub side already gets from `requiredSecrets[]`. Blog needs it immediately, since it deploys on the proxmox host through HomeAutomation-Config's Docker Compose stack and carries the copy destinations and the internal URI. The hub carries neither the directory nor a `.gitignore` entry for one today, so adopting it here comes first. - Re-vendor `repo-config/configure.sh` across the fleet. The hub swept it to one sentence per line, and it is carried `verbatim` with `appliesTo: "*"`, so every repo already holding a copy is byte-mismatched against the hub until it takes the new one. [matrix]: ./reports/conformance-matrix.md [reports]: ./reports/ +[secrets]: ./spec/secrets.json +[standup]: ./STANDUP.md