From 35af34f13050a31a4fbb261655c37a2e9e5f22bc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 18:19:51 -0700 Subject: [PATCH 01/19] test(ci): require two Runtime runner allocations --- tests/ci_contract.rs | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/tests/ci_contract.rs b/tests/ci_contract.rs index 6e8de03d..3f7063be 100644 --- a/tests/ci_contract.rs +++ b/tests/ci_contract.rs @@ -4,29 +4,37 @@ const CI_WORKFLOW: &str = include_str!("../.github/workflows/ci.yml"); const RUST_TOOLCHAIN: &str = include_str!("../rust-toolchain.toml"); const DEPENDABOT: &str = include_str!("../.github/dependabot.yml"); +#[test] +fn runtime_ci_uses_only_two_hosted_runner_allocations() { + assert_eq!(CI_WORKFLOW.matches("runs-on: ubuntu-latest").count(), 2); + assert!(CI_WORKFLOW.contains("\n coverage:\n")); + assert!(!CI_WORKFLOW.contains("\n line-coverage:\n")); + assert!(!CI_WORKFLOW.contains("\n branch-coverage:\n")); +} + #[test] fn every_checkout_is_bound_to_the_pull_request_head() { let exact_head_ref = "ref: ${{ github.event.pull_request.head.sha || github.sha }}"; - assert_eq!(CI_WORKFLOW.matches(exact_head_ref).count(), 3); + assert_eq!(CI_WORKFLOW.matches(exact_head_ref).count(), 2); } #[test] fn every_checkout_drops_persisted_credentials() { - assert_eq!(CI_WORKFLOW.matches("persist-credentials: false").count(), 3); + assert_eq!(CI_WORKFLOW.matches("persist-credentials: false").count(), 2); } #[test] fn postgres_service_image_is_immutably_pinned() { const PINNED_IMAGE: &str = "image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15"; - assert_eq!(CI_WORKFLOW.matches(PINNED_IMAGE).count(), 3); + assert_eq!(CI_WORKFLOW.matches(PINNED_IMAGE).count(), 2); let postgres_images: Vec<_> = CI_WORKFLOW .lines() .map(str::trim) .filter(|line| line.starts_with("image: postgres")) .collect(); - assert_eq!(postgres_images.len(), 3); + assert_eq!(postgres_images.len(), 2); assert!(postgres_images .iter() .all(|image| image.contains("@sha256:"))); @@ -34,7 +42,7 @@ fn postgres_service_image_is_immutably_pinned() { #[test] fn postgres_health_checks_allow_initialization_restart() { - assert_eq!(CI_WORKFLOW.matches("--health-start-period 5s").count(), 3); + assert_eq!(CI_WORKFLOW.matches("--health-start-period 5s").count(), 2); } #[test] @@ -44,12 +52,12 @@ fn postgres_credentials_are_ephemeral_per_workflow_run() { assert!(!CI_WORKFLOW.contains("POSTGRES_PASSWORD: postgres")); assert!(!CI_WORKFLOW.contains("password=postgres")); - assert_eq!(CI_WORKFLOW.matches(EPHEMERAL_PASSWORD).count(), 3); + assert_eq!(CI_WORKFLOW.matches(EPHEMERAL_PASSWORD).count(), 2); assert_eq!( CI_WORKFLOW .matches("name: Configure ephemeral PostgreSQL test connection") .count(), - 3 + 2 ); } @@ -78,6 +86,24 @@ fn lockfile_failure_evidence_is_scoped_to_the_lock_gate() { ); } +#[test] +fn coverage_runner_preserves_both_exact_gates() { + assert!(CI_WORKFLOW.contains("id: line_coverage_gate")); + assert!(CI_WORKFLOW.contains("id: branch_coverage_gate")); + assert!(CI_WORKFLOW.contains( + "if: failure() && steps.line_coverage_gate.outcome == 'failure'" + )); + assert!(CI_WORKFLOW.contains( + "if: failure() && steps.branch_coverage_gate.outcome == 'failure'" + )); + assert_eq!( + CI_WORKFLOW + .matches("cargo install cargo-llvm-cov --locked --version \"$CARGO_LLVM_COV_VERSION\"") + .count(), + 1 + ); +} + #[test] fn coverage_failures_identify_the_incomplete_source_files() { assert!(CI_WORKFLOW.contains("INCOMPLETE_FILE")); @@ -116,8 +142,6 @@ fn rust_toolchains_are_exact_and_reviewably_updated() { "rustup toolchain install 1.97.1 --profile minimal --component llvm-tools-preview"; const NIGHTLY_COVERAGE_INSTALL: &str = "rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview"; - const NIGHTLY_LLVM_COV_INSTALL: &str = - "cargo +nightly-2026-08-18 install cargo-llvm-cov --locked --version \"$CARGO_LLVM_COV_VERSION\""; const NIGHTLY_LLVM_COV_VERSION: &str = "cargo +nightly-2026-08-18 llvm-cov --version | grep -F \"$CARGO_LLVM_COV_VERSION\""; const NIGHTLY_BRANCH_JSON: &str = @@ -128,7 +152,6 @@ fn rust_toolchains_are_exact_and_reviewably_updated() { assert!(CI_WORKFLOW.contains(STABLE_QUALITY_INSTALL)); assert!(CI_WORKFLOW.contains(STABLE_COVERAGE_INSTALL)); assert!(CI_WORKFLOW.contains(NIGHTLY_COVERAGE_INSTALL)); - assert!(CI_WORKFLOW.contains(NIGHTLY_LLVM_COV_INSTALL)); assert!(CI_WORKFLOW.contains(NIGHTLY_LLVM_COV_VERSION)); assert!(CI_WORKFLOW.contains(NIGHTLY_BRANCH_JSON)); assert!(!CI_WORKFLOW.contains("nightly-2026-08-01")); From ac2c55b83de06e6a5b2a72df2914c72869a5a8f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 18:20:31 -0700 Subject: [PATCH 02/19] fix(ci): combine coverage gates on one runner --- .github/workflows/ci.yml | 108 +++++---------------------------------- 1 file changed, 12 insertions(+), 96 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9182e13c..070a4317 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,10 +112,10 @@ jobs: - name: Build warning-free documentation run: cargo doc --no-deps - line-coverage: - name: Production line coverage + coverage: + name: Production line and branch coverage runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 30 services: postgres: image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 @@ -142,18 +142,23 @@ jobs: run: | printf 'TEST_DATABASE_URL=host=localhost user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" - - name: Install pinned Rust toolchain with LLVM tools + - name: Install pinned stable Rust toolchain with LLVM tools run: rustup toolchain install 1.97.1 --profile minimal --component llvm-tools-preview + - name: Install pinned nightly with LLVM tools + run: rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview - name: Install pinned cargo-llvm-cov run: cargo install cargo-llvm-cov --locked --version "$CARGO_LLVM_COV_VERSION" - - name: Verify cargo-llvm-cov version + - name: Verify stable cargo-llvm-cov version run: cargo llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" + - name: Verify nightly cargo-llvm-cov version + run: cargo +nightly-2026-08-18 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" - name: Generate line coverage run: cargo llvm-cov --json --summary-only --output-path coverage.json - name: Enforce complete line coverage + id: line_coverage_gate run: python3 scripts/check_coverage.py coverage.json --kind lines - name: Diagnose missing line coverage - if: failure() + if: failure() && steps.line_coverage_gate.outcome == 'failure' run: | python3 - <<'PY' import json @@ -174,94 +179,5 @@ jobs: PY cargo llvm-cov report --lcov --output-path coverage-lines.lcov python3 - <<'PY' - from pathlib import Path - - workspace = Path.cwd().resolve() - source = None - for raw_line in Path("coverage-lines.lcov").read_text(encoding="utf-8").splitlines(): - if raw_line.startswith("SF:"): - source_path = Path(raw_line[3:]).resolve() - try: - source = source_path.relative_to(workspace).as_posix() - except ValueError: - source = source_path.as_posix() - elif raw_line.startswith("DA:") and source is not None: - line, hits, *_ = raw_line[3:].split(",") - if hits == "0": - print(f"::error file={source},line={line}::uncovered production line") - PY - cargo llvm-cov report --text --show-missing-lines --show-instantiations - - branch-coverage: - name: Production branch coverage - runs-on: ubuntu-latest - timeout-minutes: 15 - services: - postgres: - image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: ci_${{ github.run_id }}_${{ github.run_attempt }} - POSTGRES_DB: psychometrics_commons_test - ports: - - 5432:5432 - options: >- - --health-cmd "pg_isready -U postgres -d psychometrics_commons_test" - --health-interval 5s - --health-timeout 5s - --health-retries 10 - --health-start-period 5s - steps: - - name: Checkout exact head - uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - name: Configure ephemeral PostgreSQL test connection - shell: bash - run: | - printf 'TEST_DATABASE_URL=host=localhost user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ - "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" - - name: Install pinned nightly with LLVM tools - run: rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview - - name: Install pinned cargo-llvm-cov - run: cargo +nightly-2026-08-18 install cargo-llvm-cov --locked --version "$CARGO_LLVM_COV_VERSION" - - name: Verify cargo-llvm-cov version - run: cargo +nightly-2026-08-18 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" - - name: Generate branch coverage - run: cargo +nightly-2026-08-18 llvm-cov --branch --json --summary-only --output-path coverage-branches.json - - name: Enforce complete branch coverage - run: python3 scripts/check_coverage.py coverage-branches.json --kind branches - - name: Diagnose missing branch coverage - if: failure() - run: | - python3 - <<'PY' - import json - from pathlib import Path - - payload = json.loads(Path("coverage-branches.json").read_text(encoding="utf-8")) - entry = payload["data"][0] - kind = "branches" - for file_entry in entry.get("files", []): - summary = file_entry.get("summary", {}) - metric = summary.get(kind) - if isinstance(metric, dict) and metric.get("covered") != metric.get("count"): - print( - "INCOMPLETE_FILE " - f"{kind} {metric.get('covered')}/{metric.get('count')} " - f"{file_entry.get('filename')}" - ) - PY - cargo +nightly-2026-08-18 llvm-cov report --branch --lcov --output-path coverage-branches.lcov - python3 - <<'PY' - from pathlib import Path - - source = None - for raw_line in Path("coverage-branches.lcov").read_text(encoding="utf-8").splitlines(): - if raw_line.startswith("SF:"): - source = raw_line[3:] - elif raw_line.startswith("BRDA:"): - line, block, branch, taken = raw_line[5:].split(",", 3) - if taken in {"0", "-"}: - print(f"UNCOVERED_BRANCH {source}:{line}:{block}:{branch}") + from pathlib import import Path PY From 1ac3c739cdd83ec7c491c60f04329cfcd4f34372 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 18:21:03 -0700 Subject: [PATCH 03/19] fix(ci): restore complete combined coverage workflow --- .github/workflows/ci.yml | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 070a4317..971d345d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -179,5 +179,58 @@ jobs: PY cargo llvm-cov report --lcov --output-path coverage-lines.lcov python3 - <<'PY' - from pathlib import import Path + from pathlib import Path + + workspace = Path.cwd().resolve() + source = None + for raw_line in Path("coverage-lines.lcov").read_text(encoding="utf-8").splitlines(): + if raw_line.startswith("SF:"): + source_path = Path(raw_line[3:]).resolve() + try: + source = source_path.relative_to(workspace).as_posix() + except ValueError: + source = source_path.as_posix() + elif raw_line.startswith("DA:") and source is not None: + line, hits, *_ = raw_line[3:].split(",") + if hits == "0": + print(f"::error file={source},line={line}::uncovered production line") + PY + cargo llvm-cov report --text --show-missing-lines --show-instantiations + - name: Generate branch coverage + run: cargo +nightly-2026-08-18 llvm-cov --branch --json --summary-only --output-path coverage-branches.json + - name: Enforce complete branch coverage + id: branch_coverage_gate + run: python3 scripts/check_coverage.py coverage-branches.json --kind branches + - name: Diagnose missing branch coverage + if: failure() && steps.branch_coverage_gate.outcome == 'failure' + run: | + python3 - <<'PY' + import json + from pathlib import Path + + payload = json.loads(Path("coverage-branches.json").read_text(encoding="utf-8")) + entry = payload["data"][0] + kind = "branches" + for file_entry in entry.get("files", []): + summary = file_entry.get("summary", {}) + metric = summary.get(kind) + if isinstance(metric, dict) and metric.get("covered") != metric.get("count"): + print( + "INCOMPLETE_FILE " + f"{kind} {metric.get('covered')}/{metric.get('count')} " + f"{file_entry.get('filename')}" + ) + PY + cargo +nightly-2026-08-18 llvm-cov report --branch --lcov --output-path coverage-branches.lcov + python3 - <<'PY' + from pathlib import Path + + source = None + for raw_line in Path("coverage-branches.lcov").read_text(encoding="utf-8").splitlines(): + if raw_line.startswith("SF:"): + source = raw_line[3:] + elif raw_line.startswith("BRDA:"): + line, block, branch, taken = raw_line[5:].split(",", 3) + if taken in {"0", "-"}: + print(f"UNCOVERED_BRANCH {source}:{line}:{block}:{branch}") PY From 6970bc93e064acca56965b69d38569686c93caa0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 11:25:28 +0900 Subject: [PATCH 04/19] docs(ci): record active runtime coverage reconciliation --- CHANGELOG.md | 1 + docs/TRACEABILITY.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f91396d..a422195f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ All notable product and architecture changes are recorded here. Releases use imm ### Fixed +- **Active PR #288 only:** Runtime CI line and branch coverage now share one hosted PostgreSQL runner while retaining both exact coverage gates, exact-head checkout, immutable service-image, ephemeral-credential, and scoped-diagnostic evidence. This is not protected-main or release evidence until the PR's current head passes the required workflows and independent review. - Stale shorter assessment-session command history now fails closed instead of rewinding the current-state projection, so a later Pause/Resume still reloads after a rejected Activate-only persist. - Same-enrollment longitudinal ingest now keeps a later iOS copy and a later Android ping as distinct source identities instead of treating them as a rewrite of the first row. - Outbox delivery-lease expiry recovery now classifies liveness from the PostgreSQL clock, so a future caller timestamp cannot steal a still-live exclusive lease. diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index dc943c3f..ecf2a707 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -139,6 +139,8 @@ Still-Target logical modules/adapters include remaining product aggregate persis **Active PR** #86 anonymous-session resource authorization, plus follow-up #104, #118, #135, #144, #159, and honesty successor #225 that compare the verified actor to the supplied participant tenant/owner and session and apply a lifecycle command only after that check, is not protected-main truth until an unchanged reviewed/check-clean head is integrated. The command entry point does not accept a caller-built `ResourceScope` and does not claim the aggregates were store-loaded. Persist/reload of `assessment_participant` remains Target. Append-only identity-link history persist remains a later slice. HTTP transport remains outside this slice. Persist-backed session HTTP, exclusive outbox delivery leases, longitudinal observation clocks/membership, and claim-next scoring-job poll are already on protected main. +**Active PR** #288 is the current-protected-main landing vehicle for the Runtime CI allocation remedy. It keeps independent Rust quality checks while combining line and branch coverage on one PostgreSQL-backed runner, preserving both exact 100% coverage gates, exact pull-request-head checkout, immutable PostgreSQL image, ephemeral credentials, and scoped failure diagnostics. This operational/recovery evidence is not protected-main truth until the unchanged current-base head passes all required workflows and qualifying independent review; #286 remains historical predecessor evidence. + ## 5. ADR traceability by concern | Concern | Governing ADR(s) | From b8845193153ab0f55f78ffe9ff4e26d4493cf301 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:21:44 -0700 Subject: [PATCH 05/19] style(ci): apply pinned rustfmt --- tests/ci_contract.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/ci_contract.rs b/tests/ci_contract.rs index 3f7063be..3b5b0d41 100644 --- a/tests/ci_contract.rs +++ b/tests/ci_contract.rs @@ -90,12 +90,10 @@ fn lockfile_failure_evidence_is_scoped_to_the_lock_gate() { fn coverage_runner_preserves_both_exact_gates() { assert!(CI_WORKFLOW.contains("id: line_coverage_gate")); assert!(CI_WORKFLOW.contains("id: branch_coverage_gate")); - assert!(CI_WORKFLOW.contains( - "if: failure() && steps.line_coverage_gate.outcome == 'failure'" - )); - assert!(CI_WORKFLOW.contains( - "if: failure() && steps.branch_coverage_gate.outcome == 'failure'" - )); + assert!(CI_WORKFLOW.contains("if: failure() && steps.line_coverage_gate.outcome == 'failure'")); + assert!( + CI_WORKFLOW.contains("if: failure() && steps.branch_coverage_gate.outcome == 'failure'") + ); assert_eq!( CI_WORKFLOW .matches("cargo install cargo-llvm-cov --locked --version \"$CARGO_LLVM_COV_VERSION\"") From 4c044fe5aa96ce455666ffbf673ed755aabc148f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:48:44 -0700 Subject: [PATCH 06/19] test(ci): preserve branch evidence after line failure --- tests/ci_serial_coverage_contract.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/ci_serial_coverage_contract.rs diff --git a/tests/ci_serial_coverage_contract.rs b/tests/ci_serial_coverage_contract.rs new file mode 100644 index 00000000..2f331ad1 --- /dev/null +++ b/tests/ci_serial_coverage_contract.rs @@ -0,0 +1,17 @@ +//! Regression contract for the serialized coverage runner. +//! +//! The combined runner must preserve independent branch-coverage evidence even +//! when the line-coverage gate has already failed. This keeps the two-runner +//! allocation remedy from hiding a second coverage defect in the same run. + +const CI_WORKFLOW: &str = include_str!("../.github/workflows/ci.yml"); + +#[test] +fn branch_coverage_still_runs_after_line_coverage_failure() { + assert!(CI_WORKFLOW.contains( + "- name: Generate branch coverage\n id: branch_coverage_generation\n if: ${{ !cancelled() }}" + )); + assert!(CI_WORKFLOW.contains( + "- name: Enforce complete branch coverage\n id: branch_coverage_gate\n if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'success' }}" + )); +} From e4a5afa211d522acea9ff0bf9b8577d482bbdc49 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:52:08 -0700 Subject: [PATCH 07/19] fix(ci): keep branch coverage observable after line failure --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 971d345d..a6b837e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,9 +197,12 @@ jobs: PY cargo llvm-cov report --text --show-missing-lines --show-instantiations - name: Generate branch coverage + id: branch_coverage_generation + if: ${{ !cancelled() }} run: cargo +nightly-2026-08-18 llvm-cov --branch --json --summary-only --output-path coverage-branches.json - name: Enforce complete branch coverage id: branch_coverage_gate + if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'success' }} run: python3 scripts/check_coverage.py coverage-branches.json --kind branches - name: Diagnose missing branch coverage if: failure() && steps.branch_coverage_gate.outcome == 'failure' From 28a1982b107f688ed1e707dcecb1f7e1cf86e5b5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 00:42:00 -0700 Subject: [PATCH 08/19] test(ci): require branch-generation failure diagnostics --- tests/ci_serial_coverage_contract.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/ci_serial_coverage_contract.rs b/tests/ci_serial_coverage_contract.rs index 2f331ad1..8d3dde48 100644 --- a/tests/ci_serial_coverage_contract.rs +++ b/tests/ci_serial_coverage_contract.rs @@ -15,3 +15,13 @@ fn branch_coverage_still_runs_after_line_coverage_failure() { "- name: Enforce complete branch coverage\n id: branch_coverage_gate\n if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'success' }}" )); } + +#[test] +fn branch_generation_failure_has_operator_diagnostics() { + assert!(CI_WORKFLOW.contains( + "- name: Diagnose branch coverage generation failure\n if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'failure' }}" + )); + assert!(CI_WORKFLOW.contains( + "::error::branch coverage generation failed before the coverage gate" + )); +} From e53c552b430bdb257c3e82487a29014ebc3c70bd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 00:42:31 -0700 Subject: [PATCH 09/19] fix(ci): diagnose branch coverage generation failures --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6b837e2..7c499e6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,6 +204,9 @@ jobs: id: branch_coverage_gate if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'success' }} run: python3 scripts/check_coverage.py coverage-branches.json --kind branches + - name: Diagnose branch coverage generation failure + if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'failure' }} + run: echo "::error::branch coverage generation failed before the coverage gate" - name: Diagnose missing branch coverage if: failure() && steps.branch_coverage_gate.outcome == 'failure' run: | From f6428a0ce05304e7e72597b249eeeb3c5e4e32ab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 02:06:23 -0700 Subject: [PATCH 10/19] test(ci): require line coverage generation diagnostics --- tests/ci_serial_coverage_contract.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/ci_serial_coverage_contract.rs b/tests/ci_serial_coverage_contract.rs index 8d3dde48..bb9583e2 100644 --- a/tests/ci_serial_coverage_contract.rs +++ b/tests/ci_serial_coverage_contract.rs @@ -1,8 +1,10 @@ //! Regression contract for the serialized coverage runner. //! //! The combined runner must preserve independent branch-coverage evidence even -//! when the line-coverage gate has already failed. This keeps the two-runner -//! allocation remedy from hiding a second coverage defect in the same run. +//! when the line-coverage gate has already failed. It must also keep generation +//! failures visible as operator diagnostics instead of silently skipping the +//! corresponding gate. This keeps the two-runner allocation remedy from hiding +//! a second coverage defect in the same run. const CI_WORKFLOW: &str = include_str!("../.github/workflows/ci.yml"); @@ -16,6 +18,19 @@ fn branch_coverage_still_runs_after_line_coverage_failure() { )); } +#[test] +fn line_generation_failure_has_operator_diagnostics() { + assert!(CI_WORKFLOW.contains( + "- name: Generate line coverage\n id: line_coverage_generation" + )); + assert!(CI_WORKFLOW.contains( + "- name: Diagnose line coverage generation failure\n if: ${{ !cancelled() && steps.line_coverage_generation.outcome == 'failure' }}" + )); + assert!(CI_WORKFLOW.contains( + "::error::line coverage generation failed before the coverage gate" + )); +} + #[test] fn branch_generation_failure_has_operator_diagnostics() { assert!(CI_WORKFLOW.contains( From 4163e2533c9ae952f4b85fb074465f0584566b4b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 02:08:07 -0700 Subject: [PATCH 11/19] fix(ci): diagnose line coverage generation failure --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c499e6c..4b8f90b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,10 +153,15 @@ jobs: - name: Verify nightly cargo-llvm-cov version run: cargo +nightly-2026-08-18 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" - name: Generate line coverage + id: line_coverage_generation run: cargo llvm-cov --json --summary-only --output-path coverage.json - name: Enforce complete line coverage id: line_coverage_gate + if: ${{ !cancelled() && steps.line_coverage_generation.outcome == 'success' }} run: python3 scripts/check_coverage.py coverage.json --kind lines + - name: Diagnose line coverage generation failure + if: ${{ !cancelled() && steps.line_coverage_generation.outcome == 'failure' }} + run: echo "::error::line coverage generation failed before the coverage gate" - name: Diagnose missing line coverage if: failure() && steps.line_coverage_gate.outcome == 'failure' run: | From bf5d8cb4031cfa817cb24a338c3aa833a073b3fb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:42:32 -0700 Subject: [PATCH 12/19] test(ci): preserve coverage check identities under runner pressure --- tests/ci_serial_coverage_contract.rs | 30 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/tests/ci_serial_coverage_contract.rs b/tests/ci_serial_coverage_contract.rs index bb9583e2..950d1d6e 100644 --- a/tests/ci_serial_coverage_contract.rs +++ b/tests/ci_serial_coverage_contract.rs @@ -1,21 +1,26 @@ -//! Regression contract for the serialized coverage runner. +//! Regression contract for capacity-aware coverage scheduling. //! -//! The combined runner must preserve independent branch-coverage evidence even -//! when the line-coverage gate has already failed. It must also keep generation -//! failures visible as operator diagnostics instead of silently skipping the -//! corresponding gate. This keeps the two-runner allocation remedy from hiding -//! a second coverage defect in the same run. +//! Runtime CI must reduce peak hosted-runner pressure without renaming the two +//! long-lived coverage check identities. Branch coverage therefore waits for +//! the line-coverage job, but still runs after a line-coverage failure unless +//! the workflow was cancelled. Generation failures remain explicit operator +//! diagnostics rather than silently skipped evidence. const CI_WORKFLOW: &str = include_str!("../.github/workflows/ci.yml"); #[test] -fn branch_coverage_still_runs_after_line_coverage_failure() { - assert!(CI_WORKFLOW.contains( - "- name: Generate branch coverage\n id: branch_coverage_generation\n if: ${{ !cancelled() }}" - )); +fn coverage_jobs_preserve_their_check_identities() { + assert!(CI_WORKFLOW.contains("\n line-coverage:\n name: Production line coverage\n")); + assert!(CI_WORKFLOW.contains("\n branch-coverage:\n name: Production branch coverage\n")); + assert!(!CI_WORKFLOW.contains("name: Production line and branch coverage")); +} + +#[test] +fn branch_coverage_is_serialized_without_becoming_fail_open() { assert!(CI_WORKFLOW.contains( - "- name: Enforce complete branch coverage\n id: branch_coverage_gate\n if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'success' }}" + "\n branch-coverage:\n name: Production branch coverage\n needs: line-coverage\n if: ${{ !cancelled() }}\n" )); + assert_eq!(CI_WORKFLOW.matches("runs-on: ubuntu-latest").count(), 3); } #[test] @@ -33,6 +38,9 @@ fn line_generation_failure_has_operator_diagnostics() { #[test] fn branch_generation_failure_has_operator_diagnostics() { + assert!(CI_WORKFLOW.contains( + "- name: Generate branch coverage\n id: branch_coverage_generation" + )); assert!(CI_WORKFLOW.contains( "- name: Diagnose branch coverage generation failure\n if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'failure' }}" )); From 946f54dc8a4ecd9d0e5ce901c2c866b8302a57a6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:43:08 -0700 Subject: [PATCH 13/19] test(ci): require failed-line handoff without cancel hang --- tests/ci_serial_coverage_contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ci_serial_coverage_contract.rs b/tests/ci_serial_coverage_contract.rs index 950d1d6e..2d6615d6 100644 --- a/tests/ci_serial_coverage_contract.rs +++ b/tests/ci_serial_coverage_contract.rs @@ -18,7 +18,7 @@ fn coverage_jobs_preserve_their_check_identities() { #[test] fn branch_coverage_is_serialized_without_becoming_fail_open() { assert!(CI_WORKFLOW.contains( - "\n branch-coverage:\n name: Production branch coverage\n needs: line-coverage\n if: ${{ !cancelled() }}\n" + "\n branch-coverage:\n name: Production branch coverage\n needs: line-coverage\n if: ${{ always() && !cancelled() }}\n" )); assert_eq!(CI_WORKFLOW.matches("runs-on: ubuntu-latest").count(), 3); } From 34210dab2abeecd07fc74f9580821ffa2a0f1ceb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:43:50 -0700 Subject: [PATCH 14/19] fix(ci): serialize coverage without renaming required checks --- .github/workflows/ci.yml | 54 ++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b8f90b1..6eaac6eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,10 +112,10 @@ jobs: - name: Build warning-free documentation run: cargo doc --no-deps - coverage: - name: Production line and branch coverage + line-coverage: + name: Production line coverage runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 15 services: postgres: image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 @@ -142,16 +142,12 @@ jobs: run: | printf 'TEST_DATABASE_URL=host=localhost user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" - - name: Install pinned stable Rust toolchain with LLVM tools + - name: Install pinned Rust toolchain with LLVM tools run: rustup toolchain install 1.97.1 --profile minimal --component llvm-tools-preview - - name: Install pinned nightly with LLVM tools - run: rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview - name: Install pinned cargo-llvm-cov run: cargo install cargo-llvm-cov --locked --version "$CARGO_LLVM_COV_VERSION" - - name: Verify stable cargo-llvm-cov version + - name: Verify cargo-llvm-cov version run: cargo llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" - - name: Verify nightly cargo-llvm-cov version - run: cargo +nightly-2026-08-18 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" - name: Generate line coverage id: line_coverage_generation run: cargo llvm-cov --json --summary-only --output-path coverage.json @@ -201,9 +197,47 @@ jobs: print(f"::error file={source},line={line}::uncovered production line") PY cargo llvm-cov report --text --show-missing-lines --show-instantiations + + branch-coverage: + name: Production branch coverage + needs: line-coverage + if: ${{ always() && !cancelled() }} + runs-on: ubuntu-latest + timeout-minutes: 15 + services: + postgres: + image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ci_${{ github.run_id }}_${{ github.run_attempt }} + POSTGRES_DB: psychometrics_commons_test + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres -d psychometrics_commons_test" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + --health-start-period 5s + steps: + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Configure ephemeral PostgreSQL test connection + shell: bash + run: | + printf 'TEST_DATABASE_URL=host=localhost user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ + "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" + - name: Install pinned nightly with LLVM tools + run: rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview + - name: Install pinned cargo-llvm-cov + run: cargo +nightly-2026-08-18 install cargo-llvm-cov --locked --version "$CARGO_LLVM_COV_VERSION" + - name: Verify cargo-llvm-cov version + run: cargo +nightly-2026-08-18 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" - name: Generate branch coverage id: branch_coverage_generation - if: ${{ !cancelled() }} run: cargo +nightly-2026-08-18 llvm-cov --branch --json --summary-only --output-path coverage-branches.json - name: Enforce complete branch coverage id: branch_coverage_gate From c156b9019163900c704673af2c74789d3fc68ffa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:44:17 -0700 Subject: [PATCH 15/19] test(ci): align runner contract with serialized coverage jobs --- tests/ci_contract.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/tests/ci_contract.rs b/tests/ci_contract.rs index 3b5b0d41..6e66480c 100644 --- a/tests/ci_contract.rs +++ b/tests/ci_contract.rs @@ -5,36 +5,38 @@ const RUST_TOOLCHAIN: &str = include_str!("../rust-toolchain.toml"); const DEPENDABOT: &str = include_str!("../.github/dependabot.yml"); #[test] -fn runtime_ci_uses_only_two_hosted_runner_allocations() { - assert_eq!(CI_WORKFLOW.matches("runs-on: ubuntu-latest").count(), 2); - assert!(CI_WORKFLOW.contains("\n coverage:\n")); - assert!(!CI_WORKFLOW.contains("\n line-coverage:\n")); - assert!(!CI_WORKFLOW.contains("\n branch-coverage:\n")); +fn runtime_ci_preserves_check_identities_and_limits_peak_fanout() { + assert_eq!(CI_WORKFLOW.matches("runs-on: ubuntu-latest").count(), 3); + assert!(CI_WORKFLOW.contains("\n line-coverage:\n name: Production line coverage\n")); + assert!(CI_WORKFLOW.contains( + "\n branch-coverage:\n name: Production branch coverage\n needs: line-coverage\n if: ${{ always() && !cancelled() }}\n" + )); + assert!(!CI_WORKFLOW.contains("\n coverage:\n")); } #[test] fn every_checkout_is_bound_to_the_pull_request_head() { let exact_head_ref = "ref: ${{ github.event.pull_request.head.sha || github.sha }}"; - assert_eq!(CI_WORKFLOW.matches(exact_head_ref).count(), 2); + assert_eq!(CI_WORKFLOW.matches(exact_head_ref).count(), 3); } #[test] fn every_checkout_drops_persisted_credentials() { - assert_eq!(CI_WORKFLOW.matches("persist-credentials: false").count(), 2); + assert_eq!(CI_WORKFLOW.matches("persist-credentials: false").count(), 3); } #[test] fn postgres_service_image_is_immutably_pinned() { const PINNED_IMAGE: &str = "image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15"; - assert_eq!(CI_WORKFLOW.matches(PINNED_IMAGE).count(), 2); + assert_eq!(CI_WORKFLOW.matches(PINNED_IMAGE).count(), 3); let postgres_images: Vec<_> = CI_WORKFLOW .lines() .map(str::trim) .filter(|line| line.starts_with("image: postgres")) .collect(); - assert_eq!(postgres_images.len(), 2); + assert_eq!(postgres_images.len(), 3); assert!(postgres_images .iter() .all(|image| image.contains("@sha256:"))); @@ -42,7 +44,7 @@ fn postgres_service_image_is_immutably_pinned() { #[test] fn postgres_health_checks_allow_initialization_restart() { - assert_eq!(CI_WORKFLOW.matches("--health-start-period 5s").count(), 2); + assert_eq!(CI_WORKFLOW.matches("--health-start-period 5s").count(), 3); } #[test] @@ -52,12 +54,12 @@ fn postgres_credentials_are_ephemeral_per_workflow_run() { assert!(!CI_WORKFLOW.contains("POSTGRES_PASSWORD: postgres")); assert!(!CI_WORKFLOW.contains("password=postgres")); - assert_eq!(CI_WORKFLOW.matches(EPHEMERAL_PASSWORD).count(), 2); + assert_eq!(CI_WORKFLOW.matches(EPHEMERAL_PASSWORD).count(), 3); assert_eq!( CI_WORKFLOW .matches("name: Configure ephemeral PostgreSQL test connection") .count(), - 2 + 3 ); } @@ -87,7 +89,7 @@ fn lockfile_failure_evidence_is_scoped_to_the_lock_gate() { } #[test] -fn coverage_runner_preserves_both_exact_gates() { +fn coverage_jobs_preserve_both_exact_gates() { assert!(CI_WORKFLOW.contains("id: line_coverage_gate")); assert!(CI_WORKFLOW.contains("id: branch_coverage_gate")); assert!(CI_WORKFLOW.contains("if: failure() && steps.line_coverage_gate.outcome == 'failure'")); @@ -140,6 +142,8 @@ fn rust_toolchains_are_exact_and_reviewably_updated() { "rustup toolchain install 1.97.1 --profile minimal --component llvm-tools-preview"; const NIGHTLY_COVERAGE_INSTALL: &str = "rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview"; + const NIGHTLY_LLVM_COV_INSTALL: &str = + "cargo +nightly-2026-08-18 install cargo-llvm-cov --locked --version \"$CARGO_LLVM_COV_VERSION\""; const NIGHTLY_LLVM_COV_VERSION: &str = "cargo +nightly-2026-08-18 llvm-cov --version | grep -F \"$CARGO_LLVM_COV_VERSION\""; const NIGHTLY_BRANCH_JSON: &str = @@ -150,6 +154,7 @@ fn rust_toolchains_are_exact_and_reviewably_updated() { assert!(CI_WORKFLOW.contains(STABLE_QUALITY_INSTALL)); assert!(CI_WORKFLOW.contains(STABLE_COVERAGE_INSTALL)); assert!(CI_WORKFLOW.contains(NIGHTLY_COVERAGE_INSTALL)); + assert!(CI_WORKFLOW.contains(NIGHTLY_LLVM_COV_INSTALL)); assert!(CI_WORKFLOW.contains(NIGHTLY_LLVM_COV_VERSION)); assert!(CI_WORKFLOW.contains(NIGHTLY_BRANCH_JSON)); assert!(!CI_WORKFLOW.contains("nightly-2026-08-01")); From 0aeedcd4f3f1464550d6077bf734189c8fd691a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:45:38 -0700 Subject: [PATCH 16/19] docs(ci): describe serialized coverage without check renames --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a422195f..fd37d507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ All notable product and architecture changes are recorded here. Releases use imm ### Fixed -- **Active PR #288 only:** Runtime CI line and branch coverage now share one hosted PostgreSQL runner while retaining both exact coverage gates, exact-head checkout, immutable service-image, ephemeral-credential, and scoped-diagnostic evidence. This is not protected-main or release evidence until the PR's current head passes the required workflows and independent review. +- **Active PR #288 only:** Runtime CI preserves the long-lived `Production line coverage` and `Production branch coverage` check identities while serializing branch coverage behind line coverage. At most rust-quality plus one coverage job request hosted runners concurrently; a failed line job still hands off to branch coverage unless the workflow is cancelled. Exact-head checkout, immutable service images, ephemeral credentials, exact 100% gates, and scoped generation/gate diagnostics remain intact. This is not protected-main or release evidence until the PR's unchanged current head passes the required workflows and independent review. - Stale shorter assessment-session command history now fails closed instead of rewinding the current-state projection, so a later Pause/Resume still reloads after a rejected Activate-only persist. - Same-enrollment longitudinal ingest now keeps a later iOS copy and a later Android ping as distinct source identities instead of treating them as a rewrite of the first row. - Outbox delivery-lease expiry recovery now classifies liveness from the PostgreSQL clock, so a future caller timestamp cannot steal a still-live exclusive lease. From fd790e8205301bdd23e2fb88d8f174f4f85456bf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:49:18 -0700 Subject: [PATCH 17/19] docs(ci): drop stale single-runner traceability claim --- docs/TRACEABILITY.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index ecf2a707..5dc4fc19 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -139,8 +139,6 @@ Still-Target logical modules/adapters include remaining product aggregate persis **Active PR** #86 anonymous-session resource authorization, plus follow-up #104, #118, #135, #144, #159, and honesty successor #225 that compare the verified actor to the supplied participant tenant/owner and session and apply a lifecycle command only after that check, is not protected-main truth until an unchanged reviewed/check-clean head is integrated. The command entry point does not accept a caller-built `ResourceScope` and does not claim the aggregates were store-loaded. Persist/reload of `assessment_participant` remains Target. Append-only identity-link history persist remains a later slice. HTTP transport remains outside this slice. Persist-backed session HTTP, exclusive outbox delivery leases, longitudinal observation clocks/membership, and claim-next scoring-job poll are already on protected main. -**Active PR** #288 is the current-protected-main landing vehicle for the Runtime CI allocation remedy. It keeps independent Rust quality checks while combining line and branch coverage on one PostgreSQL-backed runner, preserving both exact 100% coverage gates, exact pull-request-head checkout, immutable PostgreSQL image, ephemeral credentials, and scoped failure diagnostics. This operational/recovery evidence is not protected-main truth until the unchanged current-base head passes all required workflows and qualifying independent review; #286 remains historical predecessor evidence. - ## 5. ADR traceability by concern | Concern | Governing ADR(s) | @@ -231,4 +229,4 @@ Nottingham, M., Wilde, E., & Dalal, S. (2023). *Problem Details for HTTP APIs* ( OpenAPI Initiative. (2025). *OpenAPI Specification, Version 3.2.0*. -AsyncAPI Initiative. (2026). *AsyncAPI Specification, Version 3.1.0*. +AsyncAPI Initiative. (2026). *AsyncAPI Specification, Version 3.1.0*. \ No newline at end of file From 0be7c7e28cbca012b964e48be34268af8a43e4e1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:50:28 -0700 Subject: [PATCH 18/19] docs(ci): preserve traceability file ending --- docs/TRACEABILITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 5dc4fc19..dc943c3f 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -229,4 +229,4 @@ Nottingham, M., Wilde, E., & Dalal, S. (2023). *Problem Details for HTTP APIs* ( OpenAPI Initiative. (2025). *OpenAPI Specification, Version 3.2.0*. -AsyncAPI Initiative. (2026). *AsyncAPI Specification, Version 3.1.0*. \ No newline at end of file +AsyncAPI Initiative. (2026). *AsyncAPI Specification, Version 3.1.0*. From eae034f62ce74bae52e0b7c91da0ef3684ca2160 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 06:38:25 -0700 Subject: [PATCH 19/19] fix(ci): format serialized coverage contract --- tests/ci_serial_coverage_contract.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/ci_serial_coverage_contract.rs b/tests/ci_serial_coverage_contract.rs index 2d6615d6..49b5e0f5 100644 --- a/tests/ci_serial_coverage_contract.rs +++ b/tests/ci_serial_coverage_contract.rs @@ -25,26 +25,24 @@ fn branch_coverage_is_serialized_without_becoming_fail_open() { #[test] fn line_generation_failure_has_operator_diagnostics() { - assert!(CI_WORKFLOW.contains( - "- name: Generate line coverage\n id: line_coverage_generation" - )); + assert!(CI_WORKFLOW + .contains("- name: Generate line coverage\n id: line_coverage_generation")); assert!(CI_WORKFLOW.contains( "- name: Diagnose line coverage generation failure\n if: ${{ !cancelled() && steps.line_coverage_generation.outcome == 'failure' }}" )); - assert!(CI_WORKFLOW.contains( - "::error::line coverage generation failed before the coverage gate" - )); + assert!( + CI_WORKFLOW.contains("::error::line coverage generation failed before the coverage gate") + ); } #[test] fn branch_generation_failure_has_operator_diagnostics() { - assert!(CI_WORKFLOW.contains( - "- name: Generate branch coverage\n id: branch_coverage_generation" - )); + assert!(CI_WORKFLOW + .contains("- name: Generate branch coverage\n id: branch_coverage_generation")); assert!(CI_WORKFLOW.contains( "- name: Diagnose branch coverage generation failure\n if: ${{ !cancelled() && steps.branch_coverage_generation.outcome == 'failure' }}" )); - assert!(CI_WORKFLOW.contains( - "::error::branch coverage generation failed before the coverage gate" - )); + assert!( + CI_WORKFLOW.contains("::error::branch coverage generation failed before the coverage gate") + ); }