Skip to content

[quality] test.yml runs vitest coverage but never publishes the report — add lcov/json-summary artifact and job summary #209

Description

@hivecommons-hive

Finding

.github/workflows/test.yml:19 runs npm run test:coverage on every push and PR, and vitest.config.mjs gates it with thresholds (85/83/91/85). But the report is never published: reporters are text + text-summary only, so the numbers exist solely in the job log. Nothing is uploaded as an artifact, written to the job summary, or sent to a coverage service.

Consequences:

  • A PR that drops coverage from 96% to 86% passes green with no visible signal — the threshold gate only fires ~10 points below the current floor (the raise in chore(coverage): raise vitest coverage thresholds to the current floor #202 narrows but does not close that window).
  • No coverage trend across main; the "did this PR add or remove coverage" question requires opening the raw log of both runs.
  • Coverage-gap analysis (this lane's and any maintainer's) has to be regenerated locally each time rather than read from CI.

Evidence, main @ 187c5e3 (run 35910387179, green): npm ci && npm run test:coverage → All files | 96.35 stmts | 94.55 branch | 99.14 funcs | 96.16 lines. The prow / prow check on PRs dogfoods the built action but emits no coverage data, so there is no end-to-end coverage source to publish either — this is purely a unit-coverage reporting gap.

Recommendation

Emit machine-readable coverage from the existing step and publish it as an artifact plus a job-summary table. Uses CLI reporter overrides so vitest.config.mjs is untouched (that file is currently held by #202).

Exact replacement for .github/workflows/test.yml — replace line 19 ( - run: npm run test:coverage) with:

      - run: >-
          npm run test:coverage --
          --coverage.reporter=text
          --coverage.reporter=text-summary
          --coverage.reporter=lcov
          --coverage.reporter=json-summary
      - name: Coverage summary
        if: always()
        run: |
          [ -f coverage/coverage-summary.json ] || exit 0
          node -e '
            const t = require("./coverage/coverage-summary.json").total;
            const row = k => `| ${k} | ${t[k].pct}% | ${t[k].covered}/${t[k].total} |`;
            console.log("### Coverage (`vitest --coverage`)\n\n| Metric | % | Covered |\n|---|---|---|");
            for (const k of ["statements","branches","functions","lines"]) console.log(row(k));
          ' >> "$GITHUB_STEP_SUMMARY"
      - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        if: always()
        with:
          name: coverage-${{ github.sha }}
          path: coverage/
          retention-days: 14

Notes for the maintainer applying this:

  • --coverage.reporter= is repeatable and replaces the config's reporter array for that invocation, so text and text-summary are restated to keep the existing log output.
  • coverage/ is already git-ignored (git check-ignore coverage → match), so the later git status --porcelain dist/ drift check is unaffected.
  • The lcov output is what a service such as Codecov consumes if the project ever wants one; no service is proposed here.
  • Pin the upload-artifact SHA against the current v4 release before applying; the SHA above is v4.6.2 at time of filing.

Why no PR: the change is entirely inside .github/workflows/. This agent's App token is minted at the contributor tier without the Workflows permission, so GitHub rejects any push touching that directory. It needs a human or an ISSUES_PRS_MERGE-tier agent to land. No part of the fix lies outside .github/workflows/ (adding reporters to vitest.config.mjs would work too, but that file is occupied by #202, and the CLI override above makes it unnecessary).

Completion criteria

  • test.yml emits lcov + json-summary coverage and uploads coverage/ as an artifact
  • Coverage table appears in the build-test job summary on a main run

Priority

  • Impact: low — coverage is high today; this is visibility, not a correctness gap
  • Effort: low

Filed by quality agent (hold-gated mode)

🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: unknown

— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityCreated by Hive for agent-filed issue provenancehive/covered-by-prHive verified that an open PR references or claims this issue; still actionable until confirmedhive/hosted-available-lke648397-260827-5q9tCreated by Hive for agent-filed issue provenancekind/cleanupCategorizes issue or PR as related to cleaning up code, process, or technical debt.needs-decisionWaiting on a maintainer decision; not contributor work until a human clears the labelqualityCreated by Hive for agent-filed issue provenancetestingCreated by Hive for agent-filed issue provenance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions