diff --git a/reports/divergences.md b/reports/divergences.md index 65e77015..291e9cab 100644 --- a/reports/divergences.md +++ b/reports/divergences.md @@ -23,8 +23,6 @@ Generated by `python3 spec/fidelity_honesty.py --report` - do not hand-edit. Cur - **TODO.md** (manifest gap, carried by Blog, HomeAutomation-Config) - A repo's own running backlog, which STANDUP.md section 2 names as one of the four declared destinations for repo-specific content. It is advisory and required by no selector, so the manifest deliberately does not declare it and a repo's copy is its own content rather than a carry. - **.husky/pre-commit** (manifest gap, carried by AudioCleaner, LanguageTags, MediaTools, NxWitness, PhotoCleaner, PlexCleaner, Utilities) - A path collision rather than a carry. Each repo's hook runs that repo's own language formatters, and the hub's runs the doc gates, so the seven downstream copies share the path and none of the content: LanguageTags and Utilities run language formatting and style with no Docker, PlexCleaner runs CSharpier and dotnet format, and the hub runs prose_lint.py and repo_gate.py. Verified by reading the downstream copies on 2026-08-10. - **scripts/README.md** (manifest gap, carried by KiCadLibrary) - A path collision rather than a carry. KiCadLibrary's copy documents its own KiCad tooling (common.py, verify_library.py, build_library.py) beside the scripts it describes, and shares nothing with the hub's fleet-gate documentation. Verified by reading it on 2026-08-10. scripts/ is a generic path, so a repo with its own tooling directory matches this check without carrying anything of the hub's. -- **.github/workflows/publish-release.yml** (manifest gap, carried by Blog, ESPHome-Config, ESPHome-NonRoot, Financial-Modeling, HomeAssistant-Config, HomeAutomation-Config, KiCadLibrary, LanguageTags, MediaTools, NxWitness, PhotoCleaner, PlexCleaner, Utilities, VSCode-Server-DotNetCore, Vantage-Config, aiopurpleair, homeassistant-purpleair) - A path collision rather than a carry, settled 2026-08-15 by reading every cataloged repo's copy on its ground-truth branch (18 copies read, the hub's own included, so 17 downstream repos carry one and 4 carry none). Each repo's publisher is its own. The job keys are per-repo (publish in 15 of the 18, against create-release on homeassistant-purpleair, publish-pypi on aiopurpleair, and an eight-job orchestration on NxWitness), and the triggers run from dispatch-only through dispatch plus schedule plus a paths-filtered push. No interface contract the schema can express fits that spread, and no appliesTo selector separates the carriers from the four that carry none, since both source-only and csharp+console repos sit on either side. What the file must honor is declared elsewhere and needs no second entry: build-release-task.yml carries the interface contract for the release path, and WORKFLOW.md's D2.3, D4.1, and D7.1 are read by hand per AUDIT.md section 5. -- **.github/workflows/validate-task.yml** (manifest gap, carried by AudioCleaner, Blog, ESPHome-NonRoot, Financial-Modeling, HomeAutomation-Config, LanguageTags, MediaTools, NxWitness, PhotoCleaner, PlexCleaner, Utilities, VSCode-Server-DotNetCore, aiopurpleair) - As publish-release.yml, and settled in the same 2026-08-15 read (14 copies read, the hub's own included, so 13 downstream repos carry one and 8 carry none). Every carrier is on: workflow_call over a per-repo job list, in six distinct shapes across the 14 (lint alone on 3, unit-test plus lint on 5, validate alone on 2, lint plus test plus docs on 2, compile-test plus lint on 1, and unit-test plus lint plus codegen-drift on 1), so the only thing every copy shares is the trigger, which no contract field states. The 8 that carry none inline their validation instead, and no selector separates them, since source-only repos again sit on either side. The fixed part is the required check the aggregator reports, which test-pull-request.yml declares at interface fidelity and the ruleset binds by name. ## Untriaged - add a disposition to `spec/divergences.json` diff --git a/reports/plexcleaner/audit.md b/reports/plexcleaner/audit.md index a3a409ca..0d172e7c 100644 --- a/reports/plexcleaner/audit.md +++ b/reports/plexcleaner/audit.md @@ -5,7 +5,7 @@ - **Workflow model:** release - **Verdict:** operational - **Date:** 2026-08-15 -- **Run stamps:** `audit run 2026-08-15T15:13:26Z | hub d54862a` (the hub `develop` head, byte-identical to `main` at `0e84805`), re-run at `audit run 2026-08-15T15:30:07Z | hub d54862a` after the two gap dispositions below were settled +- **Run stamps:** `audit run 2026-08-15T15:13:26Z | hub d54862a` (the hub `develop` head, byte-identical to `main` at `0e84805`), and `audit run 2026-08-15T17:35:45Z | hub 0713008` after the two gap dispositions below were settled by declaring both files Supersedes the 2026-07-04 snapshot, which measured the repository before the `AGENTS.md` and `GOVERNANCE.md` split, before `repo-config/configure.sh` was retired, and before the README structure sweep, so it described drift that no longer exists. This report is measured rather than carried forward: every row below was re-read at `16f196e`, and each structural workflow divergence the old snapshot recorded was re-checked rather than assumed. Two of them are closed. @@ -17,7 +17,7 @@ The repository was resynced against the hub `main` at `0e84805` and promoted on ## Dimensions -The deterministic subset is `spec/audit.py` at the run stamps above. The first reports **0 defect, letter, or error findings** and two `hub-only` drift items, both of them the fleet-wide ledger question rather than this repository's. The second, after that question was settled, reports **clean**. The judgment rows are hand-read at `16f196e`. +The deterministic subset is `spec/audit.py` at the run stamps above, and both runs report **0 defect, letter, or error findings**. The first also raised two `hub-only` drift items, the fleet-wide ledger question rather than this repository's. The second, with both files now declared at `intent`, replaces them with two staleness advisories, and reading each against the hub retires both, see Drift Finding 6. The judgment rows are hand-read at `16f196e`. | Dimension | Letter | Intent | Verdict | Evidence (file:line) | | --- | --- | --- | --- | --- | @@ -26,7 +26,7 @@ The deterministic subset is `spec/audit.py` at the run stamps above. The first r | docker | pass | pass | pass | Registry layer cache keyed `buildcache-`, never `type=gha`, reading both branches and writing only the built one on push (`build-docker-task.yml:92-95`). The trimmed overview publishes from `Docker/README.md` on `main` only (`:105-113`). The image push is gated on `inputs.push` alone, independent of the release-create skip | | python | pass | pass | pass | Lint-only profile: `RegressionTests/pyproject.toml` carries `[tool.ruff]` and `[tool.mypy]` and no `[project]` table, so there is no lockfile to pin. The tools run through `uvx` at pinned versions, `uvx ruff@0.15.22 check .`, `format --check .`, and `uvx mypy@2.3.0 .` (`validate-task.yml:98-100`), which is `python.scripts.uvx` satisfied. `python.uvlock.pinned` and `python.coverage.codecov` are N/A for this subtree, as the registry records | | branch-model | pass | pass | pass | `repo-config/configure.sh check ptr727/PlexCleaner release` from the hub checkout at `d54862a` reports every ruleset rule and every general setting matching the carried payloads, with nothing to apply | -| carried-scope | pass | pass | pass | `repo-config/configure.sh` is retired from the tree, and no other hub-hosted path is carried. The two `investigate` hits are the fleet-wide question settled in this change rather than a repository finding, see Proposed Registry / Spec Updates | +| carried-scope | pass | pass | pass | `repo-config/configure.sh` is retired from the tree, and no other hub-hosted path is carried. The two `investigate` hits were the fleet-wide question settled in this change, and both files are now declared carries rather than gaps, so this repository's copies are measured instead of unclassified, see Proposed Registry / Spec Updates | | repo-setup | pass | pass | pass | Every name in `spec/secrets.json` is present in both stores with no forbidden name, per the mechanized run. `.github/dependabot.yml` declares `nuget` and `github-actions` against both `main` and `develop`, which covers every implied ecosystem (`docker` and `devcontainers` are not implied here). `host-tools.json` declares the .NET 10 SDK alone, with the media tools argued as runtime rather than host dependencies | | linter-parity | pass | pass | pass | One config per linter drives editor, CLI, and CI: csharpier and `dotnet format style --verify-no-changes` (`validate-task.yml:64-71`), markdownlint-cli2 and cspell (`:73-85`), ruff and mypy (`:91-100`), actionlint and editorconfig-checker (`:102-106`). The `.husky/pre-commit` hook runs the C# half natively and the Markdown half through already-present Docker images | | recurring-violations | drift | pass | drift | The tree passes the mechanized `eol` check and carries no charset violation in text written since the rules took effect, but a whole-tree run of the hub prose gate reports 772 findings and `repo_gate.py --check eol-coverage` reports one. Neither is swept in CI here, because this repository consumes no prose gate. See Drift Findings 1 and 2 | @@ -52,18 +52,19 @@ None. No applicable check fails both letter and intent. ## Drift Findings -1. **CI runs no prose gate, and the tree carries the backlog that follows.** The repository vendors no prose gate and consumes none, so nothing checks a new line against the fleet prose rules at any point (`validate-task.yml` runs no such step, and `.husky/pre-commit` runs markdownlint and cspell only). A whole-tree run of the hub's `scripts/prose_lint.py` reports 772 findings, most of them `comment-wrap` (342), `comment-case` (135), `semicolon` (120), and `dash` (82), concentrated in the C# sources and in text written before the rules existed. The fleet remedy is the diff-scoped gate rather than a tree-wide sweep, so consuming the hub's `prose-gate` action in `validate-task.yml`, as PhotoCleaner does, bounds the backlog at today's size and leaves the rest to the fix-as-edited policy. Two findings are substantive rather than stylistic and can be fixed on sight: `RegressionTests/RegressionTest.sh:75,361` carry the British `behaviour`, and `PlexCleaner/VideoProps.cs:138` duplicates `not`. -2. **`.gitattributes:29` pins `*.Dockerfile` to LF and no tracked file matches it.** `repo_gate.py --check eol-coverage` fails on exactly this. The repository consolidated on a single `Docker/Dockerfile`, which the `:28` pin already covers, so the `*.Dockerfile` line is either dropped or marked forward-declared the way the `uv.lock` block above it is. +1. **CI runs no prose gate, and the tree carries the backlog that follows.** The repository vendors no prose gate and consumes none, so nothing checks a new line against the fleet prose rules at any point (`validate-task.yml` runs no such step, and `.husky/pre-commit` runs markdownlint and cspell only). A whole-tree run of the hub's `scripts/prose_lint.py` reports 772 findings, most of them `comment-wrap` (342), `comment-case` (135), `semicolon` (120), and `dash` (82), concentrated in the C# sources and in text written before the rules existed. The fleet remedy is the diff-scoped gate rather than a tree-wide sweep, so consuming the hub's `prose-gate` action in `validate-task.yml`, as PhotoCleaner does, bounds the backlog at today's size and leaves the rest to the fix-as-edited policy. Filed as ptr727/PlexCleaner#925. Two findings are substantive rather than stylistic and can be fixed on sight: `RegressionTests/RegressionTest.sh:75,361` carry the British `behaviour`, and `PlexCleaner/VideoProps.cs:138` duplicates `not`. +2. **`.gitattributes:29` pins `*.Dockerfile` to LF and no tracked file matches it.** `repo_gate.py --check eol-coverage` fails on exactly this. The repository consolidated on a single `Docker/Dockerfile`, which the `:28` pin already covers, so the `*.Dockerfile` line is either dropped or marked forward-declared the way the `uv.lock` block above it is. Filed as ptr727/PlexCleaner#926. 3. **No `changes` paths-filter (D1.1, D1.4, D1.5).** Deliberate, and documented in the workflow header. It over-builds on a docs-only or workflow-only push and cannot under-build. 4. **The dispatch guard skips rather than failing fast (D2.3).** `publish-release.yml:33`. A dispatch from a feature branch is a green run that published nothing, which reads as a successful release. 5. **Two `GOVERNANCE.md` mentions name a path this tree no longer holds.** `GOVERNANCE.md:29` names `repo-config/configure.sh`, which is the hub-hosted pointer that rule requires rather than a stale mention, and is the false positive filed as #721 and fixed in #731. `GOVERNANCE.md:282` names `.vscode/settings.json`, which this repository genuinely deleted, so that one is a real stale mention for the file's next edit. +6. **Both `intent` staleness advisories on the newly declared workflows are false trails, read and retired here.** The advisory is date-based by design, a hint rather than proof, and `intent` equivalence is a human call. `validate-task.yml` last changed 2026-08-05 against a hub canonical that moved on 2026-08-14, but that hub change added `ruff format --check` and this copy already runs it, at a pinned version rather than `@latest` per its own convention (`validate-task.yml:99`). `publish-release.yml` last changed 2026-07-24 against a hub canonical that moved on 2026-08-08, and that hub change was a Dependabot action-pin bump, which is per-repo drift by definition. Neither copy trails the hub in substance. The publisher case is not this repository's at all: the advisory has no action-pin normalization, so a Dependabot bump in the hub marks every carrier at once, 17 of them in this run, filed as #735. ## Proposed Registry / Spec Updates - **Delete the branch-hygiene `driftNote`.** It reads "3 stale Dependabot nuget branches (PRs closed/superseded) and an unmerged feature/727-decouple-release-assets branch linger". The Dependabot branches are gone, and the live list now holds seven branches beyond `main` and `develop` rather than four, so the note describes neither the old state nor the new one. Branch hygiene is also not a baseline deviation any check scores, which is what a `driftNote` records, so it is deleted rather than re-snapshotted into prose that rots the same way. Applied in this change. - **The other two `driftNotes` stand.** `ARCHITECTURE.md` and `codecov.yml` are both carried beyond the baseline, and the csharp-plus-python shape note is accurate down to its details (the `RegressionTests/` subtree, no `uv.lock`, a `pyproject.toml` holding only ruff and mypy config, and the two N/A check ids). - **`status`, `types`, `publish`, `requiredSecrets`, `consumerModel`, `releaseTrigger`, and `configLayout` are unchanged and accurate.** -- **Settle both `investigate` gap dispositions as `accepted`.** Applied in this change, with the measurement written into the ledger reason. Neither `.github/workflows/publish-release.yml` nor `.github/workflows/validate-task.yml` can be declared. Reading every cataloged repository's copy on its ground-truth branch shows the publisher's job keys are per-repo (`publish` in 15 of the 18 copies read, against `create-release`, `publish-pypi`, and an eight-job orchestration elsewhere) and its triggers run from dispatch-only to dispatch plus schedule plus push, while `validate-task.yml` is uniformly `on: workflow_call` over a per-repo job list in six distinct shapes. The counts here include the hub's own copy, so 17 downstream repositories carry a publisher and 13 carry a validator, which is what the ledger's carrier lists name. No `interface` contract the schema can express fits either file, and no `appliesTo` selector separates the carriers from the non-carriers, since `source-only` repos sit on both sides of each. What governs them is already declared: `test-pull-request.yml` and `build-release-task.yml` carry the interface contracts, and WORKFLOW.md's D-guarantees are read by hand per AUDIT.md section 5. +- **Settle both `investigate` gap dispositions by declaring the files at `intent` fidelity.** Applied in this change, and it is a fleet decision rather than this repository's, taken by the maintainer on the measurement below. `spec/files.json` gains `.github/workflows/publish-release.yml` (`appliesTo` the three publishing `releaseTrigger` values) and `.github/workflows/validate-task.yml` (`appliesTo` `*`), and both ledger rows are deleted, since a declared file is no longer a gap. What the measurement settled is the fidelity rather than the question of carrying: reading every cataloged repository's copy on its ground-truth branch shows the publisher's job keys are per-repo (`publish` in 15 of the 18 copies read, against `create-release`, `publish-pypi`, and an eight-job orchestration elsewhere) and its triggers run from dispatch-only to dispatch plus schedule plus push, while `validate-task.yml` is uniformly `on: workflow_call` over a per-repo job list in six distinct shapes. No `interface` contract fits that spread, and `intent` is the tier that does: presence asserted, equivalence judged by hand against the `intentRef`, and a staleness advisory raised when the hub's copy moves past a repository's, which is what keeps a copy close to the canonical rather than merely present. The publisher's scope is exact, since every repository lacking one carries `releaseTrigger: none` and all 18 carriers carry a publishing trigger. The validator is declared fleet-wide by decision rather than by measurement, so the eight repositories that inline their validation into `test-pull-request.yml` now carry a presence finding, which is the convergence this asks for rather than noise. - **Set the `csharp` plus `python` row's Last audited to 2026-08-15** in `reports/conformance-matrix.md`. Applied in this change. ## Escalations diff --git a/spec/divergences.json b/spec/divergences.json index 3daf54f0..a39be82b 100644 --- a/spec/divergences.json +++ b/spec/divergences.json @@ -11,8 +11,6 @@ { "path": "TODO.md", "disposition": "accepted", "reason": "A repo's own running backlog, which STANDUP.md section 2 names as one of the four declared destinations for repo-specific content. It is advisory and required by no selector, so the manifest deliberately does not declare it and a repo's copy is its own content rather than a carry.", "tracking": null }, { "path": ".husky/pre-commit", "disposition": "accepted", "reason": "A path collision rather than a carry. Each repo's hook runs that repo's own language formatters, and the hub's runs the doc gates, so the seven downstream copies share the path and none of the content: LanguageTags and Utilities run language formatting and style with no Docker, PlexCleaner runs CSharpier and dotnet format, and the hub runs prose_lint.py and repo_gate.py. Verified by reading the downstream copies on 2026-08-10.", "tracking": null }, { "path": "scripts/README.md", "disposition": "accepted", "reason": "A path collision rather than a carry. KiCadLibrary's copy documents its own KiCad tooling (common.py, verify_library.py, build_library.py) beside the scripts it describes, and shares nothing with the hub's fleet-gate documentation. Verified by reading it on 2026-08-10. scripts/ is a generic path, so a repo with its own tooling directory matches this check without carrying anything of the hub's.", "tracking": null }, - { "path": "pyproject.toml", "disposition": "investigate", "reason": "The hub gained a config-only Scripts-profile pyproject.toml in #388. Decide whether to track it (intent, appliesTo python) after confirming the python repos carry an equivalent.", "tracking": null }, - { "path": ".github/workflows/publish-release.yml", "disposition": "accepted", "reason": "A path collision rather than a carry, settled 2026-08-15 by reading every cataloged repo's copy on its ground-truth branch (18 copies read, the hub's own included, so 17 downstream repos carry one and 4 carry none). Each repo's publisher is its own. The job keys are per-repo (publish in 15 of the 18, against create-release on homeassistant-purpleair, publish-pypi on aiopurpleair, and an eight-job orchestration on NxWitness), and the triggers run from dispatch-only through dispatch plus schedule plus a paths-filtered push. No interface contract the schema can express fits that spread, and no appliesTo selector separates the carriers from the four that carry none, since both source-only and csharp+console repos sit on either side. What the file must honor is declared elsewhere and needs no second entry: build-release-task.yml carries the interface contract for the release path, and WORKFLOW.md's D2.3, D4.1, and D7.1 are read by hand per AUDIT.md section 5.", "tracking": null }, - { "path": ".github/workflows/validate-task.yml", "disposition": "accepted", "reason": "As publish-release.yml, and settled in the same 2026-08-15 read (14 copies read, the hub's own included, so 13 downstream repos carry one and 8 carry none). Every carrier is on: workflow_call over a per-repo job list, in six distinct shapes across the 14 (lint alone on 3, unit-test plus lint on 5, validate alone on 2, lint plus test plus docs on 2, compile-test plus lint on 1, and unit-test plus lint plus codegen-drift on 1), so the only thing every copy shares is the trigger, which no contract field states. The 8 that carry none inline their validation instead, and no selector separates them, since source-only repos again sit on either side. The fixed part is the required check the aggregator reports, which test-pull-request.yml declares at interface fidelity and the ruleset binds by name.", "tracking": null } + { "path": "pyproject.toml", "disposition": "investigate", "reason": "The hub gained a config-only Scripts-profile pyproject.toml in #388. Decide whether to track it (intent, appliesTo python) after confirming the python repos carry an equivalent.", "tracking": null } ] } diff --git a/spec/files.json b/spec/files.json index 04933a04..46a61f67 100644 --- a/spec/files.json +++ b/spec/files.json @@ -28,6 +28,8 @@ { "path": "spec/secrets.json", "fidelity": "intent", "intentRef": "docs/repo-config-carry.md", "appliesTo": "*" }, { "path": ".github/dependabot.yml", "appliesTo": "*" }, { "path": ".github/workflows/test-pull-request.yml", "fidelity": "interface", "contract": { "requiredJobKeys": ["check-workflow-status"], "requiredCheckName": "Check pull request workflow status job" }, "intentRef": "GOVERNANCE.md#workflow-yaml-conventions", "appliesTo": "*" }, + { "path": ".github/workflows/validate-task.yml", "fidelity": "intent", "intentRef": "WORKFLOW.md#d1---pr-fast-feedback-smoke", "appliesTo": "*" }, + { "path": ".github/workflows/publish-release.yml", "fidelity": "intent", "intentRef": "WORKFLOW.md#d4---release--publish", "appliesTo": ["two-phase", "dispatch-only", "publish-on-merge"] }, { "path": ".github/workflows/merge-bot-pull-request.yml", "fidelity": "interface", "contract": { "requiredJobKeys": ["merge-dependabot", "disable-auto-merge-on-maintainer-push"] }, "intentRef": "WORKFLOW.md#d8---bots--automation", "appliesTo": "*" }, { "path": ".github/workflows/build-release-task.yml", "fidelity": "interface", "contract": { "requiredJobKeys": ["get-version", "validate-release", "github-release"], "artifactNameToken": "release-asset-", "requireTokensInJob": { "github-release": ["pattern:", "merge-multiple:"] }, "forbidTokensInJob": { "github-release": ["artifact-ids:"] }, "verbatimJobs": ["github-release"] }, "reference": "catalog/snippets/workflows/build-release-task.yml", "intentRef": "GOVERNANCE.md#release-model", "appliesTo": ["csharp", "console", "docker", "nuget", "pypi", "eda"] }, { "path": ".github/workflows/deploy-site.yml", "fidelity": "interface", "contract": { "requiredJobKeys": ["assert-ref", "validate", "deploy"], "requireTokensInJob": { "deploy": ["deploy-site-task.yml", "secrets: inherit"] } }, "reference": "catalog/snippets/workflows/deploy-site.yml", "intentRef": "WORKFLOW.md#d4---release--publish", "appliesTo": ["hugo"] },