refactor(capabilities): readiness_command_recommendation takes a stru… #1762
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Coverage Policy: See docs/COVERAGE_POLICY.md for full details | |
| # | |
| # Phased Threshold Schedule (br-2r76): | |
| # Phase 1 (Current): 60% - Foundation | |
| # Phase 2 (Q2 2026): 70% - Stability | |
| # Phase 3 (Q3 2026): 80% - Confidence | |
| # Phase 4 (Q4 2026): 90% - Excellence | |
| # | |
| # Update COVERAGE_THRESHOLD when advancing phases. | |
| name: Coverage | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| # NOTE: `-D warnings` intentionally NOT set here. The coverage job compiles | |
| # sibling path-dep crates via `[patch]` in Cargo.toml, which makes them | |
| # behave like workspace members and bypass cargo's default `--cap-lints | |
| # allow` for dependencies. Upstream warnings (e.g. fsqlite-pager | |
| # dead_code) must not fail the coverage run — lint enforcement lives in | |
| # the Lint job (ci.yml). | |
| # Phased coverage threshold - update per docs/COVERAGE_POLICY.md schedule | |
| COVERAGE_THRESHOLD: 60 | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| coverage: | |
| name: Test Coverage | |
| runs-on: ubuntu-latest | |
| # llvm-cov instrumented tests take noticeably longer than plain `cargo | |
| # test` (2-3x). Previously the job short-circuited at the compile step | |
| # so 30 min sufficed; now it gets through compile + ~3500 tests, which | |
| # can push past 30 min on a cold cache. Match CI's `test-rust` matrix | |
| # timeout (45 min) for safety. | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| with: | |
| components: llvm-tools-preview | |
| - name: Install cargo-llvm-cov | |
| uses: taiki-e/install-action@8db66d64862314dae6eb34821203eb85fcbc5055 # cargo-llvm-cov | |
| - name: Cache cargo registry | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-coverage-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-coverage- | |
| ${{ runner.os }}-cargo- | |
| - name: Generate coverage report | |
| env: | |
| # Deterministic test execution | |
| RUST_TEST_THREADS: 1 | |
| PROPTEST_CASES: 32 # Fixed seed count for property tests | |
| # llvm-cov instrumentation balloons stack frames enough to overflow | |
| # the default 2 MiB thread stack inside clap derive parsers | |
| # (pages_cli_flag_tests). Bump to 16 MiB to match what cargo test | |
| # uses natively for deeply nested generated code. | |
| RUST_MIN_STACK: "16777216" | |
| run: | | |
| # Run with deterministic options and skip known flaky tests | |
| cargo llvm-cov --workspace --lib -j 1 \ | |
| --ignore-filename-regex "(tests/|benches/)" \ | |
| --codecov \ | |
| --output-path codecov.json \ | |
| -- --skip install_sh --skip install_ps1 | |
| - name: Upload to Codecov | |
| uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4 | |
| with: | |
| files: codecov.json | |
| fail_ci_if_error: false | |
| verbose: true | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Generate coverage summary (JSON) | |
| run: | | |
| # Generate JSON coverage report (reuses instrumented data from previous step) | |
| cargo llvm-cov report \ | |
| --ignore-filename-regex "(tests/|benches/)" \ | |
| --json \ | |
| --output-path coverage.json | |
| # Extract coverage percentage | |
| COVERAGE=$(jq -r '.data[0].totals.lines.percent // 0' coverage.json) | |
| echo "## Test Coverage: ${COVERAGE}%" >> $GITHUB_STEP_SUMMARY | |
| # Phased threshold indicators (see docs/COVERAGE_POLICY.md) | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Phase Progress" >> $GITHUB_STEP_SUMMARY | |
| if (( $(echo "$COVERAGE >= 90" | bc -l) )); then | |
| echo ":trophy: **Phase 4 Complete** - Excellence (90%+)" >> $GITHUB_STEP_SUMMARY | |
| elif (( $(echo "$COVERAGE >= 80" | bc -l) )); then | |
| echo ":star: **Phase 3 Complete** - Confidence (80%+)" >> $GITHUB_STEP_SUMMARY | |
| elif (( $(echo "$COVERAGE >= 70" | bc -l) )); then | |
| echo ":white_check_mark: **Phase 2 Complete** - Stability (70%+)" >> $GITHUB_STEP_SUMMARY | |
| elif (( $(echo "$COVERAGE >= 60" | bc -l) )); then | |
| echo ":heavy_check_mark: **Phase 1 Complete** - Foundation (60%+)" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo ":x: **Below Phase 1** - Needs improvement (<60%)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "_See [Coverage Policy](../docs/COVERAGE_POLICY.md) for targets_" >> $GITHUB_STEP_SUMMARY | |
| - name: Generate gap report | |
| run: | | |
| COVERAGE_THRESHOLD=${{ env.COVERAGE_THRESHOLD }} \ | |
| ./scripts/generate-gap-report.sh coverage.json gap-report.md | |
| # Add top uncovered modules to job summary | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Top Coverage Gaps" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Extract top 5 uncovered modules | |
| jq -r '.data[0].files[] | | |
| select(.filename | contains("/src/")) | | |
| select(.summary.lines.count > 100) | | |
| { | |
| filename: (.filename | split("/src/") | last | "src/" + .), | |
| uncovered: (.summary.lines.count - .summary.lines.covered), | |
| percent: (if .summary.lines.count > 0 then (.summary.lines.covered * 100 / .summary.lines.count | floor) else 0 end) | |
| } | | |
| select(.uncovered > 200) | | |
| "| \(.percent)% | \(.uncovered) | `\(.filename)` |"' coverage.json | \ | |
| sort -t'|' -k3 -nr | head -5 | { | |
| echo "| Coverage | Uncovered Lines | Module |" | |
| echo "|----------|-----------------|--------|" | |
| cat | |
| } >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "_See gap-report.md artifact for full analysis_" >> $GITHUB_STEP_SUMMARY | |
| - name: Check coverage threshold | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| COVERAGE=$(jq -r '.data[0].totals.lines.percent // 0' coverage.json) | |
| # Use environment variable for phased threshold (see docs/COVERAGE_POLICY.md) | |
| THRESHOLD=${{ env.COVERAGE_THRESHOLD }} | |
| echo "Coverage: ${COVERAGE}%" | |
| echo "Threshold: ${THRESHOLD}% (Phase 1 - Foundation)" | |
| echo "Policy: See docs/COVERAGE_POLICY.md for phased targets" | |
| if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then | |
| echo "::error::Coverage ${COVERAGE}% is below ${THRESHOLD}% threshold" | |
| echo "::error::Add tests for new code or see docs/COVERAGE_POLICY.md for exclusion process" | |
| exit 1 | |
| fi | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: coverage-report | |
| path: | | |
| codecov.json | |
| coverage.json | |
| gap-report.md | |
| retention-days: 14 |