# Codecov config. Targets and ignore patterns mirror sonar-project.properties so # the two services agree on what's measured. If you change one, change both. coverage: precision: 1 round: nearest range: "65...90" status: project: default: # PROJECT-LEVEL ROLLUP IS INFORMATIONAL, NOT GATING (#227). # # Codecov's project rollup has repeatedly reported numbers that don't match reality on this repo: PR #224 (head # bd59ef76) showed project/server at 37.9% and project/ui at 42.3% while the per-package go-test runs were # producing 70%+ coverage and the per-PR Sonar new-code gate was passing at >80%. Two earlier rounds (#124, then # again at #227) responded by dropping the threshold (80 → 75 → 70). Main drifted below the floor each time and # the gate became a chronic-flake red check that every PR in the M13 stack carried a "documented skip reason" for. # # `informational: true` makes the project rollup visible in the codecov dashboard without failing the PR check. # Sonar's per-PR "Coverage on New Code" gate (configured in the SonarCloud UI at 80%) remains the authoritative # bar for what each PR must meet; codecov's per-PATCH gate below still gates on new-code coverage, just like # Sonar does. Demoting the project rollup means we stop chasing the codecov-vs-reality discrepancy on every PR # and let Sonar's machinery (which has been reliable) carry the new-code coverage contract. # # If a real coverage cliff lands on main, the trend will show up in the project rollup's number; an operator # noticing a regression flips informational back to false and tightens the target. The path back is short. informational: true target: 70% threshold: 1% only_pulls: false patch: default: # PATCH-LEVEL GATE STAYS ENFORCING. The chronic flake was the project rollup, not the patch coverage; patch # gates have consistently passed in the M13 stack. Per-PR new-code coverage is what we actually want to gate on, # and the patch gate enforces that here. Sonar's 80% new-code gate is parallel; codecov is held at 70% so it # doesn't gate-out PRs that Sonar already accepts. target: 70% threshold: 0% if_ci_failed: error # Per-component flags so the Codecov dashboard distinguishes Go (agent), # Go (server), and TS (ui). Each flag is uploaded by its own action call; # see .github/workflows/test.yml. flag_management: default_rules: carryforward: true statuses: - type: project # Per-flag project rollups (agent / server / ui) are informational for the same reason the overall project # rollup is: codecov's per-flag numbers have been unreliable on this repo and were the most frequent chronic # flake in the M13 stack ("codecov/project/ui at 14.4% vs 70% target" on Go-only PRs that didn't touch the UI # tree at all). Demote to informational; sonar's per-PR new-code gate remains the authoritative bar. informational: true target: 70% - type: patch # Per-flag patch target stays enforcing. The chronic flake was the rollup, not the patch; patch gates pin # new-code coverage per PR and have been consistently honest. Held at 70%; sonar's 80% new-code gate is # parallel. target: 70% individual_flags: - name: agent paths: - agent/ - name: server paths: - server/ # The `ui` flag covers ui/src/**. Two LCOVs upload under it: the # vitest unit-test LCOV (ui/coverage/lcov.info) and the Playwright # E2E LCOV (test/e2e/coverage/lcov-e2e.info, V8 coverage of the # running React bundle via monocart-coverage-reports). Codecov takes # the union per flag, so a line hit by either suite counts as covered. - name: ui paths: - ui/src/ # Path fixes. vitest's v8 provider writes the UI LCOV (ui/coverage/lcov.info) with paths relative to ui/, e.g. # `SF:src/components/ProcessTree.tsx`, NOT repo-relative. The `ui` flag above is scoped to `ui/src/` and PR diff paths are # `ui/src/...`, so without a fix codecov can't map the LCOV onto the diff and reports changed UI lines as 0% (issue #468). # This prefix map re-roots those `src/` paths to repo paths. Codecov anchors the prefix at the start of the path (the # config validator normalizes `src/` to `^src/`), so it does NOT touch the Playwright E2E LCOV (already `ui/src/`-rooted, # does not start with `src/`) or the Go profiles (`agent/`, `server/`). Do NOT write the `^` yourself: codecov re-anchors # the input, so an explicit `^src/` becomes the dead regex `^^src/` and the fix silently stops matching. Sonar reads the # same vitest LCOV via sonar.javascript.lcov.reportPaths and resolves the `src/` root itself, so it needs no equivalent. fixes: - "src/::ui/src/" comment: layout: "header, diff, flags, components, files" behavior: default require_changes: false # Mirror sonar.coverage.exclusions so the two services agree. Generated code, # main entrypoints, test harnesses, the Swift extensions (no Go coverage), # the JSON-Schema sources, and the test/ directory all stay off the # coverage surface. ui/src/** is NO LONGER in the ignore list: the # Playwright E2E run captures UI coverage via V8 + source maps, so we # want those lines to count. Keep sonar-project.properties' # sonar.coverage.exclusions in sync. ignore: - "**/cmd/**/main.go" # Process-startup wiring (the platform sensor seam) is untestable glue in the same class as main.go; the receiver loops it # spawns lived inline in main.go (excluded above) before ADR-0018 extracted them into per-platform sensors*.go files. - "**/cmd/**/sensors*.go" - "ui/vite.config.ts" # UI test scaffolding (setup.ts, shared fixture factories). vitest's coverage config already excludes "src/test/**", so the # collector never instruments these files and the LCOV contains no records for them. Without a matching ignore here, codecov reads # "changed lines, no coverage data" as 0% and fails the patch gate on any edit to a test helper, which is a reporting artefact # rather than a real coverage gap. Their logic is exercised by ui/src/test/*.test.ts. - "ui/src/test/**" - "server/detection/testharness/**" - "schema/**" - "extension/edr/**" - "agent/receiver/**" - "server/bootstrap/**" - "server/httpserver/**" - "test/**" - "tools/**" # Platform-specific Go files compile only on their target GOOS, so the Linux/macOS coverage runners never exercise them. # Sonar excludes the same globs (sonar.coverage.exclusions); keep the two lists in sync. - "**/*_windows.go" - "**/*_other.go"