Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
54d5ac9
test: require complete frontend production coverage scope
seonghobae Sep 3, 2026
db0968b
fix(coverage): measure all production TypeScript
seonghobae Sep 3, 2026
a50b0a2
test(coverage): require exact-head fail-closed evidence
seonghobae Sep 3, 2026
05ce06b
test(coverage): add bounded command diagnostic helper
seonghobae Sep 3, 2026
8b69c6d
fix(coverage): restore exact-head fail-closed evidence pipeline
seonghobae Sep 3, 2026
a0cbddc
merge: restack coverage foundation on release verifier owner
seonghobae Sep 3, 2026
0431be3
merge: restack coverage foundation on path-filter repair
seonghobae Sep 3, 2026
b988db7
test(ci): require failed coverage metric diagnostics
seonghobae Sep 3, 2026
bb5aeda
fix(ci): preserve failed coverage metric diagnostics
seonghobae Sep 3, 2026
017ea94
test(ci): require Rust coverage line-target evidence
seonghobae Sep 3, 2026
cbe6aba
fix(ci): preserve Rust coverage line targets
seonghobae Sep 3, 2026
75254c6
test(ci): inherit coverage diagnostic contracts
seonghobae Sep 3, 2026
52b79da
test(ci): inherit executable coverage diagnostic contract
seonghobae Sep 3, 2026
c7d75c7
test(ci): inherit hosted runner disk budget contract
seonghobae Sep 3, 2026
ff0a6f2
fix(ci): bound Rust feature-batch disk and linker use
seonghobae Sep 3, 2026
b6fa431
test(ci): inherit Rust evidence concurrency contract
seonghobae Sep 3, 2026
f68c150
test(ci): reproduce stale-head Test queue retention
seonghobae Sep 3, 2026
58834dd
fix(ci): cancel obsolete first-attempt Test runs
seonghobae Sep 3, 2026
d5513be
docs(ci): restore current exact-head coverage evidence contract
seonghobae Sep 3, 2026
3a231db
test(ci): require coverage contract docs to trigger Test
seonghobae Sep 3, 2026
5ccf354
fix(ci): test coverage contract documentation changes
seonghobae Sep 3, 2026
7330fd8
merge: restack coverage foundation on release contract inheritance
seonghobae Sep 3, 2026
af39bce
test: inherit cloud review format-control coverage
seonghobae Sep 3, 2026
1d264e9
restack(ci): adopt current path-filter foundation
seonghobae Sep 4, 2026
c820ab5
test(commands): inherit current-compatible coverage evidence
seonghobae Sep 4, 2026
33beca9
test(cloud): inherit current-compatible coverage evidence
seonghobae Sep 4, 2026
44e872f
docs(coverage): bind current measured gap recovery
seonghobae Sep 4, 2026
b478fc1
test(coverage): restore iCloud sync-health public evidence
seonghobae Sep 4, 2026
b46ed29
docs(coverage): record iCloud donor adaptation
seonghobae Sep 4, 2026
ba1918c
chore(stack): adopt bounded Vitest workers with coverage config
seonghobae Sep 4, 2026
2223430
test: restore real Git worktree safety coverage
seonghobae Sep 4, 2026
8d3ce22
docs: record real worktree coverage adoption
seonghobae Sep 4, 2026
a287c12
chore(stack): restack coverage owner on path-filter foundation
seonghobae Sep 4, 2026
969f790
test(ci): align coverage workflow with canonical supersession contract
seonghobae Sep 4, 2026
dfc0737
chore(stack): restack coverage owner on exact path-filter parent
seonghobae Sep 4, 2026
3813044
test(coverage): preserve dirty secondary worktrees
seonghobae Sep 4, 2026
1394402
test: preserve prunable worktrees as evidence gaps
seonghobae Sep 4, 2026
0126d9d
chore: restack coverage owner on current path-filter parent
seonghobae Sep 4, 2026
7d9d504
chore(stack): restack coverage owner on current path-filter parent
seonghobae Sep 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/scripts/bound-coverage-command-diagnostic.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 2 ]]; then
printf 'usage: %s <raw-log> <bounded-log>\n' "$0" >&2
exit 64
fi

raw_log="$1"
bounded_log="$2"
max_total_bytes=32768
edge_bytes=9000
error_focus_bytes=8000
other_focus_bytes=4000
max_line_bytes=2048
line_bounded_log="${bounded_log}.line-bounded.$$"
error_focus_log="${bounded_log}.error-focus.$$"
other_focus_log="${bounded_log}.other-focus.$$"

cleanup() {
rm -f "$line_bounded_log" "$error_focus_log" "$other_focus_log"
}
trap cleanup EXIT

LC_ALL=C awk -v max_bytes="$max_line_bytes" '{
if (length($0) > max_bytes) {
print substr($0, 1, max_bytes) " ... [line truncated]"
} else {
print
}
}' "$raw_log" > "$line_bounded_log"

diagnostic_bytes="$(wc -c < "$line_bounded_log" | tr -d ' ')"
if (( diagnostic_bytes <= max_total_bytes )); then
cp "$line_bounded_log" "$bounded_log"
else
LC_ALL=C awk '
{
plain = $0
gsub(/\033\[[0-9;]*m/, "", plain)
}
plain ~ /^thread .* panicked at / {
print
panic_context = 6
in_error = 0
next
}
panic_context > 0 {
print
panic_context--
next
}
plain ~ /^(error(\[[^]]+\])?:|fatal:|Caused by:)/ {
print
in_error = 1
next
}
in_error && plain ~ /^[[:space:]]*(--> |[0-9]+[[:space:]]*\||\|[[:space:]]|= (note|help):|(note|help):)/ {
print
next
}
{ in_error = 0 }
' "$line_bounded_log" > "$error_focus_log"

LC_ALL=C awk '
{
plain = $0
gsub(/\033\[[0-9;]*m/, "", plain)
}
plain ~ /^warning(\[[^]]+\])?:|^[[:space:]]*= (note|help):|^[[:space:]]*(note|help):/ { print }
' "$line_bounded_log" > "$other_focus_log"

head -c "$edge_bytes" "$line_bounded_log" > "$bounded_log"
if [[ -s "$error_focus_log" ]]; then
printf '\n--- focused compiler errors and test panics ---\n' >> "$bounded_log"
head -c "$error_focus_bytes" "$error_focus_log" >> "$bounded_log"
fi
if [[ -s "$other_focus_log" ]]; then
printf '\n--- focused compiler warnings and notes ---\n' >> "$bounded_log"
head -c "$other_focus_bytes" "$other_focus_log" >> "$bounded_log"
fi
printf '\n--- bounded diagnostic tail ---\n' >> "$bounded_log"
tail -c "$edge_bytes" "$line_bounded_log" >> "$bounded_log"
fi
235 changes: 221 additions & 14 deletions .github/workflows/test.yml

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions docs/development/coverage-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Exact-head coverage evidence

DiskSage treats coverage as CI evidence bound to one immutable source head. A locally reported percentage, a predecessor workflow run, or a successful test exit code is not equivalent evidence.

## Exact source identity

The `Test` workflow checks out `${{ github.event.pull_request.head.sha || github.sha }}` explicitly in every checkout-bearing job. The coverage job copies the same value into `HEAD_SHA`, validates it as a 40-character commit SHA, and records it as both `head_sha` and `commit_sha` in `coverage-evidence.json`.

This deliberately distinguishes the pull-request source head from GitHub's synthetic merge commit and from the PR's historical base snapshot. A coverage artifact is valid only for the exact head that produced it.

## Rust measurement boundary

The Rust evidence job uses a dated nightly toolchain with `llvm-tools-preview` and runs:

```text
cargo llvm-cov --locked --no-cfg-coverage --no-cfg-coverage-nightly --all-features --manifest-path src-tauri/Cargo.toml --branch --json --output-path coverage.json
```

`--branch` is explicit because branch coverage is part of the repository gate. The dated nightly is intentional because cargo-llvm-cov documents branch coverage as unstable/nightly-dependent.

`--no-cfg-coverage` and `--no-cfg-coverage-nightly` are also intentional. Instrumentation must not silently alter DiskSage production `cfg` semantics and thereby change the code graph being claimed by the gate. Ordinary exact-head Rust tests and the feature-specific CLI/library proofs exercise effectful boundaries separately.

The JSON report is the sole source for emitted percentages. The evidence builder requires non-empty, finite, fully covered totals and exactly 100% for:

- statement-equivalent LLVM region coverage;
- branch coverage;
- function coverage; and
- line coverage.

Missing or malformed totals, zero denominators, partial coverage, or identity drift prevent `coverage-evidence.json` from being produced as passing evidence.

## Failure evidence

Coverage failure must remain actionable without leaking runner-local paths or unbounded command output.

If `cargo llvm-cov` produces `coverage.json` but exits because a metric is below the required threshold, `Build exact-head coverage evidence` still runs under `always() && hashFiles('coverage.json') != ''`. It writes `coverage-diagnostic.json` with repository-relative high-gap files and up to 40 sorted uncovered line numbers per file, then the exact-100% validation fails closed before the success artifact can be emitted.

If measurement itself fails before usable JSON exists, `.github/scripts/bound-coverage-command-diagnostic.sh` produces a bounded command diagnostic. The helper caps pathological individual lines, preserves both bounded log edges, prioritizes compiler errors and test panic context, and retains ANSI-colored Rust diagnostics after normalization. The workflow removes raw transient logs after redaction. If diagnostic rendering or redaction fails, the authoritative coverage exit status is preserved and the only replacement text is:

```text
coverage diagnostic rendering failed; raw diagnostic withheld
```

The same bounded diagnostic identity is carried in the artifact name with the exact head SHA. Failure diagnostics are not passing coverage evidence.

## Frontend scope

Vitest coverage includes source-controlled production TypeScript under `src/lib/**/*.ts` and `src/routes/**/*.ts`, excluding tests and generated declaration files. It does not use a hand-maintained production-file allowlist. Statement, branch, function, and line thresholds remain exactly 100%.

A frontend failure produces a bounded diagnostic with repository-relative file identity and uncovered line coordinates. It does not relax the threshold or remove production files from the denominator.

## Hosted-runner resource contract

The ordinary Test job contains several large Rust feature batches. To avoid treating hosted-runner disk/linker exhaustion as a product defect while still preserving the actual proofs:

- duplicate-audit and archive library checks use `--lib` so Cargo does not relink unrelated integration-test targets for a focused library proof;
- their dedicated CLI proofs remain explicit and `--locked`;
- `cargo clean --manifest-path src-tauri/Cargo.toml` reclaims disposable Cargo build artifacts between the duplicate-audit and archive batches; and
- `df -h .` leaves bounded disk-availability evidence in the workflow log.

The cleanup does not delete source, lockfiles, coverage thresholds, or test contracts.

## Concurrency and stale-head runs

Repeated commits to one pull-request branch can otherwise leave obsolete Test runs queued while a newer exact head becomes authoritative. The workflow therefore uses a same-ref concurrency group:

```yaml
concurrency:
group: test-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.run_attempt == 1 }}
```

The group intentionally omits source SHA so a newer first-attempt run can supersede older first-attempt work for the same ref. A manual or automated rerun (`run_attempt > 1`) is not made to self-cancel by this condition. Canceled or superseded runs remain non-passing; only the unchanged current head can supply merge evidence.

GitHub documents concurrency groups as the mechanism for limiting simultaneous workflow/job execution and canceling outdated runs. It also documents workflow artifacts as the mechanism for persisting outputs such as test and coverage results after a job completes.

## Latest measured gap and recovery

The latest completed repository-wide Rust measurement that produced usable coverage totals is predecessor head `af39bce9bb6ac3186e3940e2c94dd8381080f619` from Test run `33779617794`. It measured 64,391/80,218 regions (80.270014%), 5,292/8,991 branches (58.858859%), 3,357/4,924 functions (68.176280%), and 42,867/53,111 lines (80.712094%). Those values are RED evidence: none is reusable as passing evidence for a later head.

The bounded diagnostic identified `src-tauri/src/commands.rs` (2,364 uncovered lines), `src-tauri/src/cloud.rs` (687), `src-tauri/src/icloud_sync_health.rs` (474), and `src-tauri/src/provider_oauth.rs` (388) as the largest then-current uncovered production contributors. The same diagnostic also recorded `provider_api_write.rs` (385), `podman_reclaim.rs` (330), `zotero_local.rs` (299), and `git_worktree.rs` (286). Recovery is ordered by measured contribution and valid owner evidence rather than arbitrary test-file count.

Current coverage-owner lineage has adopted the still-valid command-layer public coverage and HOME-absent environment fixture from historical PR #156, then adopted `src-tauri/tests/cloud_public_coverage.rs` after verifying its public cloud contracts against current `cloud.rs`. These are test-only changes; they do not narrow the denominator or substitute synthetic data for destructive/recovery acceptance. Their value is not considered inherited until an unchanged exact head compiles, runs them, and emits the next real measurement.

Historical `src-tauri/tests/icloud_sync_health_public_coverage.rs` remained semantically valid but was not source-compatible verbatim because current `IcloudSyncHealthReport` added the optional `native_status` and `file_provider_activity` evidence fields. Commit `b478fc174a7b77a60e960053715e55d282a5c166` restores the donor on the current coverage owner with only those two fields bound to `None`; the filesystem/admission assertions and source-database non-mutation checks are otherwise unchanged. This is adopted pending unchanged-head compile/runtime evidence and the next real repository-wide measurement; the source-shape repair is not itself passing coverage evidence.

`provider_oauth.rs` remains owned by provider-OAuth hardening PR #339, so the coverage branch does not copy its domain tests. Among the next inspected historical donors, `podman_reclaim_public_coverage.rs` relies on synthetic Podman JSON and a fake executable for its broad success path, so it is not used as destructive/recovery acceptance evidence here. Commit `222343048b8a8d99e47275e12643032666726645` instead restores `git_worktree_public_coverage.rs`, whose fixtures create real temporary Git repositories and whose mutation case creates and removes only a real temporary secondary worktree after exact approval, while verifying the branch is retained. That adoption targets the measured 286-line `git_worktree.rs` gap without altering production code or weakening the coverage denominator. It remains pending unchanged-head hosted compile/runtime evidence and a new real measurement.

## Operating rule

A missing `coverage-evidence` artifact is not passing. Queued, pending, canceled, failed, stale-head, malformed, less-than-100%, predecessor, or synthetic-merge evidence is non-passing. Engineers must add realistic tests or remove genuinely unreachable production code; they must not lower thresholds, hard-code percentages, narrow the production denominator, or reuse an artifact from a different head.

## References

GitHub. (2026). *Concurrency*. GitHub Docs. https://docs.github.com/en/actions/concepts/workflows-and-actions/concurrency

GitHub. (2026). *Workflow artifacts*. GitHub Docs. https://docs.github.com/en/actions/concepts/workflows-and-actions/workflow-artifacts

Endo, T. (2026). *cargo-llvm-cov: Cargo subcommand to easily use LLVM source-based code coverage* [Computer software]. GitHub. https://github.com/taiki-e/cargo-llvm-cov
41 changes: 41 additions & 0 deletions src-tauri/src/commands_env_coverage_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! Coverage for command-layer environment branches that must remain deterministic and side-effect safe.

#[cfg(not(windows))]
use crate::commands::list_roots;

#[cfg(not(windows))]
struct EnvRestore {
key: &'static str,
value: Option<std::ffi::OsString>,
}

#[cfg(not(windows))]
impl EnvRestore {
fn remove(key: &'static str) -> Self {
let value = std::env::var_os(key);
std::env::remove_var(key);
Self { key, value }
}
}

#[cfg(not(windows))]
impl Drop for EnvRestore {
fn drop(&mut self) {
match self.value.take() {
Some(value) => std::env::set_var(self.key, value),
None => std::env::remove_var(self.key),
}
}
}

#[cfg(not(windows))]
#[test]
fn list_roots_does_not_invent_a_home_root_when_home_is_absent() {
// The Rust test workflow is serialized (`RUST_TEST_THREADS=1`), so temporarily removing HOME
// cannot race another DiskSage unit test. Restore it even if the assertion unwinds.
let _restore = EnvRestore::remove("HOME");

let roots = list_roots();

assert_eq!(roots, vec!["/".to_string()]);
}
Loading
Loading