diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e42d6cb..a184cc75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -318,6 +318,56 @@ jobs: # compile without it, even for tests that never touch the embedded # assets themselves. - run: go test . ./internal/... -race -coverprofile=cover.out && bash scripts/check-go-coverage.sh cover.out + # Consumed by diff-coverage below -- reuse the profile this job + # already produced instead of paying a second full -race run. + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: go-coverprofile + path: cover.out + + # Changed-lines coverage (goal 0109 phase 2, the clean-as-you-code + # counterpart to the gocognit gate): diff-cover reports how much of + # THIS PR's changed lines the unit layers cover, from the Go + # coverprofile (via gcov2lcov) + Vitest's lcov. INFORMATIONAL for + # now -- continue-on-error and outside ci-gate's needs -- because + # this repo's layering deliberately proves components in e2e, not + # unit tests, so an industry-default fail-under would fail honest UI + # PRs; the floor gets set from a track record of real PR numbers, + # the same measure-then-promote shape as webview-bridge-smoke. + diff-coverage: + needs: [changes, frontend, test-go] + if: success() && github.event_name == 'pull_request' && needs.changes.outputs.code == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + continue-on-error: true + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: go-coverprofile + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: '1.25' + cache: true + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + - run: npm ci + working-directory: frontend + - run: npm run test + working-directory: frontend + - run: go install github.com/jandelgado/gcov2lcov@v1.1.1 + - run: gcov2lcov -infile cover.out -outfile go-lcov.info + - run: pipx install diff-cover + - name: Changed-lines coverage report + run: | + diff-cover go-lcov.info frontend/coverage/lcov.info \ + --compare-branch "origin/${{ github.base_ref }}" \ + --show-uncovered e2e: needs: [changes, frontend] diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8bed7c33..adef0cc3 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -90,6 +90,10 @@ export default defineConfig({ exclude: [...configDefaults.exclude, "e2e/**"], coverage: { provider: "v8", + // lcov alongside the default text output: the changed-lines + // coverage gate (goal 0109, diff-cover in CI) consumes + // coverage/lcov.info; text stays for humans. + reporter: ["text", "lcov"], // Hand-written source only -- generated Wails bindings are // exempt for the same we-don't-own-their-shape reason // scripts/check-loc.sh exempts them. diff --git a/internal/services/mcpsvc/millmcpservice_atlas_test.go b/internal/services/mcpsvc/millmcpservice_atlas_test.go index a563a135..cf8f65c2 100644 --- a/internal/services/mcpsvc/millmcpservice_atlas_test.go +++ b/internal/services/mcpsvc/millmcpservice_atlas_test.go @@ -124,6 +124,7 @@ func TestAtlasMCP_ListKinds_IncludesSeededTopicWithDeclaredFields(t *testing.T) } if topic == nil { t.Fatalf("seeded Topic kind not found in atlas_list_kinds: %+v", out.Kinds) + return // staticcheck SA5011: t.Fatalf's noreturn fact is lost under CI's build config } fieldKeys := map[string]bool{} for _, f := range topic.Fields { @@ -186,6 +187,7 @@ func TestAtlasMCP_SearchCards_MatchesTitleAndReportsParent(t *testing.T) { } if titleMatch == nil { t.Fatalf("atlas_search_cards(Ada) = %+v, want a match on Ada Lovelace's own title", out.Matches) + return // staticcheck SA5011: t.Fatalf's noreturn fact is lost under CI's build config } if titleMatch.ParentID != exampleArea.ID { t.Errorf("match.ParentID = %q, want %q", titleMatch.ParentID, exampleArea.ID) diff --git a/internal/webviewbridgesmoke/checks_test.go b/internal/webviewbridgesmoke/checks_test.go index bc348af5..c7606f8e 100644 --- a/internal/webviewbridgesmoke/checks_test.go +++ b/internal/webviewbridgesmoke/checks_test.go @@ -362,6 +362,7 @@ func TestCheckStickyBorderColorFlip(t *testing.T) { } if createCall == nil { t.Fatal("CreateNote was never called") + return // staticcheck SA5011: t.Fatal's noreturn fact is lost under CI's build config } args, _ := createCall.args["args"].([]any) if len(args) != 3 || args[2] != "space-1" {