Implementation Plan: SonarCloud QA config — projectVersion + coverage-scope + review tool
Branch: feat/sonar-qa-config-remediation | Mission: sonar-qa-config-remediation-01KWYCX7 Spec: kitty-specs/sonar-qa-config-remediation-01KWYCX7/spec.md Provenance: reuses @stijn-dejongh's Concern-B implementation concerns (his projectVersion-wiring, coverage-scope, and review-tool work) + research.md decisions from ci-hygiene-and-sonar-debt-remediation-01KWV531.
Summary
Fix how SonarCloud measures and how coverage is interpreted for this repo — three independent, low-blast-radius surfaces, no product-code change. Concern is CI/QA config + one read-only tool + one doc.
Technical Context
Language/Version: Python 3.11 (repo pinned) for the smoke test + config-assertion; Bash for the review tool; GitHub Actions YAML for the wiring Primary Dependencies: GitHub Actions (.github/workflows/ci-quality.yml), pyproject.toml (version source, read via tomllib), curl + SonarCloud public REST API (GET /api/measures/component_tree, /api/project_analyses/search — token-free read endpoints for this public project), pytest (smoke test), jq (tool output shaping) Storage: N/A — flat committed config (ci-quality.yml), one docs/ note, one scripts/ci/ script + its recorded test fixture Testing: pytest fixture-backed smoke test for the tool (token-free, tests/ci/), a config-assertion for the projectVersion wiring (SC-001a), shellcheck on the script Target Platform: GitHub Actions CI (Ubuntu) + local Project Type: single project — CI/QA config + tooling (no product-code change) Performance Goals: N/A — config + a read-only query tool; the smoke test uses a recorded fixture (no network in CI) Constraints: no SONAR_TOKEN dependency (NFR-001, public read endpoints only); no suppression/ratchet/allowlist (NFR-002); research-first for #2422 (C-002); scope = 3-part slice, backlog-slicing excluded (C-001); ruff+mypy clean, commitlint-clean Scale/Scope: ci-quality.yml (projectVersion wiring), scripts/ci/sonarcloud_branch_review.sh + its test (authored fresh), docs/guides/coverage-signals.md (reconciliation note)
Charter Check
Read-only observability + config; no product surface, no new secret, no debt masking — aligned with the charter's honesty + no-ratchet standing orders.
Implementation Concerns
IC-01 — Wire sonar.projectVersion from pyproject.toml (FR-001, FR-002) — P1
- Surface:
.github/workflows/ci-quality.yml— thesonarcloudjob's "Materialize effective Sonar config" step (and/or the scanner-actionargs). - Approach: read the version from
pyproject.toml(single canonical source — no hardcode, no duplicate key insonar-project.properties) and inject-Dsonar.projectVersion=<version>(scanner arg) OR appendsonar.projectVersion=<version>to the materialized effective properties. Survives a version bump with zero further edits (FR-002). - Verify (SC-001a): a static YAML-parse config-assertion confirms the wiring is present in the
sonarcloudjob — no live analysis (the job is gated toschedule/workflow_dispatchonly, not PR/push). Baseline reset (SC-001b) lands on the *next nightly cron (17 2) or a manual dispatch**, not on merge.
IC-02 — Coverage-scope investigation → reconciliation doc (FR-003, FR-004) — P2, research-first
- Approach (C-002, research-first): use the promoted tool (IC-03) against the unauthenticated API —
GET /api/measures/component_tree?component=Priivacy-ai_spec-kitty&metricKeys=coverage— to enumerate the file set SonarCloud scores, and compare it against the internaldiff-coveragegate's critical-path file list (.github/workflows/ci-quality.ymldiff-cover step). Determine whether the file sets materially differ or only the threshold philosophy (whole-repo average vs per-PR-diff-only) differs. - Deliverable: a committed
docs/note (e.g.docs/guides/coverage-signals.md) explaining the two metrics, why they differ, and how to classify a discrepancy as a real regression vs an expected scope difference; referenced from the coverage/quality-gate context so a PR reviewer finds it (FR-004). Code change only if a genuinesources/exclusionsmisconfiguration is found — not for a philosophy-only difference.
IC-03 — Author the read-only SonarCloud review tool + smoke test (FR-005, FR-006) — P2
- Approach: author
scripts/ci/sonarcloud_branch_review.shfresh (no git-fetchable predecessor exists — post-spec finding). Read-only subcommands over the public REST API: quality-gate status, coverage metrics, per-file uncovered lines, issues-by-rule/file, and project-version/analyses (/api/project_analyses/search+/api/components/show) — the subcommand that backs SC-001b's confirmation (closes the post-plan orphan-dependency finding). Arg-parsing + explicit error handling; no state mutation. Sibling toscripts/ci/quality_gate_decision.py;shellcheck-clean. - Smoke test (FR-006, NFR-001): a committed test (
pytestdriving the script, or a shell test the CI runs) exercises arg-parsing + output shape against a recorded fixture (so it needs no network + noSONAR_TOKEN), plus one optional live-read smoke against the public API. Verifies read-only behavior.
Constraints honored
- C-001 scope: the ~900-issue backlog slicing + issue-filing automation + roadmap-slice fixes are OUT (epic #1928). This tool is the read-only query layer only.
- NFR-001 no new secret: every step works against public read endpoints; the smoke test uses a recorded fixture.
- NFR-002 no masking: no suppression/ratchet/allowlist; the coverage work clarifies interpretation, it does not hide findings.
Project Structure (files touched)
.github/workflows/ci-quality.yml # IC-01: inject sonar.projectVersion from pyproject.toml
scripts/ci/sonarcloud_branch_review.sh # IC-03: NEW read-only SonarCloud REST tool
tests/ci/test_sonarcloud_branch_review.* # IC-03: NEW token-free smoke test (fixture-backed)
tests/ci/ or tests/architectural/ # IC-01: NEW config-assertion (projectVersion wired) for SC-001a
docs/guides/coverage-signals.md # IC-02: NEW coverage-scope reconciliation note (FR-003/004)
Key Decisions
- projectVersion source (Stijn DM-01KWV7EJ…):
pyproject.tomlversion; resets baseline per bump; briefly reports an unreleased mid-cycle version — accepted (baseline-reset signal, not release authority). - Research-first coverage (C-002): docs if only threshold philosophy differs; config change only for a genuine file-set misconfig.
- Author-fresh tool (post-spec finding): the predecessor script is untracked/gitignored on every ref — not fetchable; author to the behavioral contract.
- Trigger-gate + testability (post-plan finding): the
sonarcloudjob runs only onschedule/workflow_dispatch(not PR/push) — so SC-001a is a static YAML-parse assertion, and SC-001b lands on the next nightly cron / manual dispatch, not on merge. (The token/fork-secrets model applies to other jobs, not this one.)